Search code examples
c#entity-framework-corememory-leaksgarbage-collectionbulkinsert

EF core. Bulk methods. Memory leak?


I have a program that transfers a table from 1 database to another. The table contains 24k records.

I create contexts using a factory.

Program.cs

builder.Services.AddDbContextFactory<StackFlContext>(x =>
            {
                x.UseSqlServer(builder.Configuration.GetConnectionString("StackFLDb"));
                x.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
            });
            builder.Services.AddDbContextFactory<StackUlContext>(x =>
            {
                x.UseSqlServer(builder.Configuration.GetConnectionString("StackULDb"));
                x.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
            });
            builder.Services.AddDbContextFactory<SabpekContext>(x =>
            {
                x.UseSqlServer(builder.Configuration.GetConnectionString("SabpekDb"));
                x.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
            });

I transfer the table like this:

public class IntegrationStackReferencesService : IIntegrationReferencesService
    {
        private readonly IDbContextFactory<StackFlContext> _stackFLContextFactory;
        private readonly IDbContextFactory<StackUlContext> _stackULContextFactory;
        private readonly IDbContextFactory<SabpekContext> _sabpekContextFactory;
        private readonly ILogger _logger;

        public IntegrationStackReferencesService(IDbContextFactory<StackFlContext> stackFLContextFactory,
                                                 IDbContextFactory<StackUlContext> stackULContextFactory,
                                                 IDbContextFactory<SabpekContext> sabpekContextFactory,
                                                 ILogger logger
            )
        {
            _stackFLContextFactory = stackFLContextFactory;
            _stackULContextFactory = stackULContextFactory;
            _sabpekContextFactory = sabpekContextFactory;
            _logger = logger;
        }

        public async Task SyncStackReferences()
        {
            //Каждый блок защищён try catch поэтому если какой-то справочник не синхронизируется или будет ошибка,
            //то методы синхронизации других, всё равно будут выполнены
            using (var stackFlContext = _stackFLContextFactory.CreateDbContext())
            using (var stackUlContext = _stackULContextFactory.CreateDbContext())
            using (var sabpekContext = _sabpekContextFactory.CreateDbContext())
            {
                await SyncTechStructureAsync<Models.Stack.StackFl.ВозможнаяСтруктура>(stackFlContext, sabpekContext);
            }
        }
private async Task SyncTable<T>(DbContext dbContext, IQueryable<T> list) where T : class
        {
            dbContext.Set<T>().Truncate();
            dbContext.Set<T>().BulkInsert(list);
            //await dbContext.SaveChangesAsync();
        }

As a result, before the start of the transfer, I have the following memory consumption enter image description here

After transfer:

enter image description here

I expect the memory should be cleared to +- frist value, no? what am i doing wrong? Contexts should have been cleaned up after "using". Why the memory is not cleared and what should I do to clear it?


Solution

  • This is not a sign of memory leak at all.

    Garbage collection (GC) in .NET is not constantly freeing memory. A quote from the documentation:

    Garbage collection occurs when one of the following conditions is true:

    -- The system has low physical memory. The memory size is detected by either the low memory notification from the operating system or low memory as indicated by the host.

    -- The memory that's used by allocated objects on the managed heap surpasses an acceptable threshold. This threshold is continuously adjusted as the process runs.

    I suggest you read more about GC here.

    The memory consumption in your example has increased to merely 127 MB. This is too little on any modern hardware and is not considered worth the effort by GC.