Why ASP Net Core 2.2 do not release memory?

!!!LAST EDIT: THIS SOLUTION IS NOT THE BEST WAY, SOME MIGHT SAY THIS IS A TERRIBLE SOLUTION... I RECOMMEND THE ANSWER BELOW!!!

I was struggling with the same issue. After a little research i found that .NET Core allocating new memory space if it can. It will be released if it is necessary, but this release is more resource demanding than the other releasing, because you have to use the GC this way:

GC.Collect(2, GCCollectionMode.Forced, true);
GC.WaitForPendingFinalizers();

My solution was to create a middleware:

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace RegisterValidator
{
    public class GCMiddleware
    {
        private readonly RequestDelegate _next;

        public GCMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            await _next(httpContext);
            GC.Collect(2, GCCollectionMode.Forced, true);
            GC.WaitForPendingFinalizers();
        }
    }
}

EDIT: this middleware will provide a GC.Collect after every call.

And register it in the Configure() method in Startup.cs:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider service, ILoggerFactory loggerFactory)
    {

        app.UseMiddleware<GCMiddleware>();
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseStaticFiles();
        //app.UseHttpsRedirection();
        app.UseMvcWithDefaultRoute();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("\"No ");

        });
    }

Maybe there is another solution but I haven't found it yet. I hope it helps.

Edit: If you aren't sure that the memory usage is because there is a huge amount of allodated space of some other issue you can use dotMemory that will profile your memory usage with more detail than your Visual Studio.


ASP.NET Core can appear to use more memory than it should because it's configured to use Server GC by default (as opposed to Workstation GC). For a discussion of a similar concern see here. In theory, the application should be able to reduce its memory footprint when your server faces a memory pressure.

More on this topic in this MSDN doc and Github CoreCLR doc.

To see if it's indeed Server GC that causes the additional memory consumption, you can set GC strategy to workstation in your csproj file:

<PropertyGroup> 
    <ServerGarbageCollection>false</ServerGarbageCollection>
</PropertyGroup>

TL;DR; Just make sure your DBContext is not Transient.

I tried both the answers but in my case issue was entirely different. At the start of the project one of the devs, for some experiment, made the application context's lifetime to be transient. The default life time of the context variable is scoped and which is what it should be, at least in our case.

services.AddDbContext<AppContext>(ServiceLifetime.Transient);

To keep context as scoped you can inject it following two ways:

  • services.AddDbContext<AppContext>();
  • services.AddDbContext<AppContext>(ServiceLifetime.Scoped);

Brief Explanation: Just like any other service in API, DBContext can also be injected with lifetime as

  1. Transient - Creates new instance of DBContext for each repository call
  2. Scoped - Creates one instance of DBContext for one API call
  3. Singleton - Creates once instance of DBContext for all API calls

So if you make DBContext Transient and your one API call is getting data from 10 different repos. This will create 10 different instance of DBContext. The memory consumption will be very quick and GC will not have enough time to release the memory. This can crash the site in less than an hour with just 2-3 users.