Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory consumption, memory leaks #35369

Open
Meteoeoeo opened this issue Dec 22, 2024 · 4 comments
Open

Memory consumption, memory leaks #35369

Meteoeoeo opened this issue Dec 22, 2024 · 4 comments

Comments

@Meteoeoeo
Copy link

Meteoeoeo commented Dec 22, 2024

Memory consumption, memory leaks after db operations

Image

Code

Sample: https://github.com/Meteoeoeo/memory_consumption

after the first RunSeeder call the consumption increases significantly, in the next ones it increases but less

InitDatabase(app);
RunSeeder(app, connectionString);
RunSeeder(app, connectionString);
RunSeeder(app, connectionString);
RunSeeder(app, connectionString);
RunSeeder(app, connectionString);
void InitDatabase(WebApplication webApplication)
{
    using var scope = webApplication.Services.CreateScope();
    using var context = scope.ServiceProvider.GetRequiredService<EmployeeContext>();
    context.Database.EnsureDeleted();
    context.Database.EnsureCreated();
}

void RunSeeder(WebApplication webApplication, string connectionString)
{
    using var scope = webApplication.Services.CreateScope();
    using var context = scope.ServiceProvider.GetRequiredService<EmployeeContext>();
    // tested with manually create EmployeeContext - the same consumption
    //var optionsBuilder = new DbContextOptionsBuilder<EmployeeContext>();
    //optionsBuilder.UseSqlServer(connectionString);
    //using var context = new EmployeeContext(optionsBuilder.Options);
    var seeder = new EmployeeSeeder();
    seeder.Seed(context);
}

without batches the consumption is greater

   public void Seed(EmployeeContext context)
   {
       // if (!context.Employees.Any())
       {
           var random = new Random();
           var departments = new[] { "IT", "HR", "Finance", "Marketing", "Sales" };

           var employees = Enumerable.Range(1, 100_000).Select(i => new Employee
           {
               FirstName = $"FirstName{i}",
               LastName = $"LastName{i}",
               Department = departments[random.Next(departments.Length)],
               IsActive = random.Next(0, 2) == 1,
               HireDate = DateTime.Now.AddDays(-random.Next(0, 3650))
           }).ToList();

           var batchSize = 1_000;
           for (int i = 0; i < employees.Count; i += batchSize)
           {
               var batch = employees.Skip(i).Take(batchSize).ToList();
               context.Employees.AddRange(batch);
               context.SaveChanges();
               context.ChangeTracker.Clear(); // not solve the problem
           }
       }
   }

some queries from logs

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[@p0='?' (Size = 50), @p1='?' (Size = 100), @p2='?' (DbType = DateTime2), @p3='?' (DbType = Boolean), @p4='?' (Size = 100), @p5='?' (Size = 50), @p6='?' (Size = 100), @p7='?' (DbType = DateTime2), @p8='?' (DbType = Boolean), @p9='?' (Size = 100), @p10='?' (Size = 50), @p11='?' (Size = 100), @p12='?' (DbType = DateTime2), @p13='?' (DbType = Boolean), @p14='?' (Size = 100), @p15='?' (Size = 50), @p16='?' (Size = 100), @p17='?' (DbType = DateTime2), @p18='?' (DbType = Boolean), @p19='?' (Size = 100), @p20='?' (Size = 50), @p21='?' (Size = 100), @p22='?' (DbType = DateTime2), @p23='?' (DbType = Boolean), @p24='?' (Size = 100), @p25='?' (Size = 50), @p26='?' (Size = 100), @p27='?' (DbType = DateTime2), @p28='?' (DbType = Boolean), @p29='?' (Size = 100), @p30='?' (Size = 50), @p31='?' (Size = 100), @p32='?' (DbType = DateTime2), @p33='?' (DbType = Boolean), @p34='?' (Size = 100), @p35='?' (Size = 50), @p36='?' (Size = 100), @p37='?' (DbType = DateTime2), @p38='?' (DbType = Boolean), @p39='?' (Size = 100), @p40='?' (Size = 50), @p41='?' (Size = 100), @p42='?' (DbType = DateTime2), @p43='?' (DbType = Boolean), @p44='?' (Size = 100), @p45='?' (Size = 50), @p46='?' (Size = 100), @p47='?' (DbType = DateTime2), @p48='?' (DbType = Boolean), @p49='?' (Size = 100), @p50='?' (Size = 50), @p51='?' (Size = 100), @p52='?' (DbType = DateTime2), @p53='?' (DbType = Boolean), @p54='?' (Size = 100), @p55='?' (Size = 50), @p56='?' (Size = 100), @p57='?' (DbType = DateTime2), @p58='?' (DbType = Boolean), @p59='?' (Size = 100), @p60='?' (Size = 50), @p61='?' (Size = 100), @p62='?' (DbType = DateTime2), @p63='?' (DbType = Boolean), @p64='?' (Size = 100), @p65='?' (Size = 50), @p66='?' (Size = 100), @p67='?' (DbType = DateTime2), @p68='?' (DbType = Boolean), @p69='?' (Size = 100), @p70='?' (Size = 50), @p71='?' (Size = 100), @p72='?' (DbType = DateTime2), @p73='?' (DbType = Boolean), @p74='?' (Size = 100), @p75='?' (Size = 50), @p76='?' (Size = 100), @p77='?' (DbType = DateTime2), @p78='?' (DbType = Boolean), @p79='?' (Size = 100), @p80='?' (Size = 50), @p81='?' (Size = 100), @p82='?' (DbType = DateTime2), @p83='?' (DbType = Boolean), @p84='?' (Size = 100), @p85='?' (Size = 50), @p86='?' (Size = 100), @p87='?' (DbType = DateTime2), @p88='?' (DbType = Boolean), @p89='?' (Size = 100), @p90='?' (Size = 50), @p91='?' (Size = 100), @p92='?' (DbType = DateTime2), @p93='?' (DbType = Boolean), @p94='?' (Size = 100), @p95='?' (Size = 50), @p96='?' (Size = 100), @p97='?' (DbType = DateTime2), @p98='?' (DbType = Boolean), @p99='?' (Size = 100), @p100='?' (Size = 50), @p101='?' (Size = 100), @p102='?' (DbType = DateTime2), @p103='?' (DbType = Boolean), @p104='?' (Size = 100), @p105='?' (Size = 50), @p106='?' (Size = 100), @p107='?' (DbType = DateTime2), @p108='?' (DbType = Boolean), @p109='?' (Size = 100), @p110='?' (Size = 50), @p111='?' (Size = 100), @p112='?' (DbType = DateTime2), @p113='?' (DbType = Boolean), @p114='?' (Size = 100), @p115='?' (Size = 50), @p116='?' (Size = 100), @p117='?' (DbType = DateTime2), @p118='?' (DbType = Boolean), @p119='?' (Size = 100), @p120='?' (Size = 50), @p121='?' (Size = 100), @p122='?' (DbType = DateTime2), @p123='?' (DbType = Boolean), @p124='?' (Size = 100), @p125='?' (Size = 50), @p126='?' (Size = 100), @p127='?' (DbType = DateTime2), @p128='?' (DbType = Boolean), @p129='?' (Size = 100), @p130='?' (Size = 50), @p131='?' (Size = 100), @p132='?' (DbType = DateTime2), @p133='?' (DbType = Boolean), @p134='?' (Size = 100), @p135='?' (Size = 50), @p136='?' (Size = 100), @p137='?' (DbType = DateTime2), @p138='?' (DbType = Boolean), @p139='?' (Size = 100), @p140='?' (Size = 50), @p141='?' (Size = 100), @p142='?' (DbType = DateTime2), @p143='?' (DbType = Boolean), @p144='?' (Size = 100), @p145='?' (Size = 50), @p146='?' (Size = 100), @p147='?' (DbType = DateTime2), @p148='?' (DbType = Boolean), @p149='?' (Size = 100), @p150='?' (Size = 50), @p151='?' (Size = 100), @p152='?' (DbType = DateTime2), @p153='?' (DbType = Boolean), @p154='?' (Size = 100), @p155='?' (Size = 50), @p156='?' (Size = 100), @p157='?' (DbType = DateTime2), @p158='?' (DbType = Boolean), @p159='?' (Size = 100), @p160='?' (Size = 50), @p161='?' (Size = 100), @p162='?' (DbType = DateTime2), @p163='?' (DbType = Boolean), @p164='?' (Size = 100), @p165='?' (Size = 50), @p166='?' (Size = 100), @p167='?' (DbType = DateTime2), @p168='?' (DbType = Boolean), @p169='?' (Size = 100), @p170='?' (Size = 50), @p171='?' (Size = 100), @p172='?' (DbType = DateTime2), @p173='?' (DbType = Boolean), @p174='?' (Size = 100), @p175='?' (Size = 50), @p176='?' (Size = 100), @p177='?' (DbType = DateTime2), @p178='?' (DbType = Boolean), @p179='?' (Size = 100), @p180='?' (Size = 50), @p181='?' (Size = 100), @p182='?' (DbType = DateTime2), @p183='?' (DbType = Boolean), @p184='?' (Size = 100), @p185='?' (Size = 50), @p186='?' (Size = 100), @p187='?' (DbType = DateTime2), @p188='?' (DbType = Boolean), @p189='?' (Size = 100), @p190='?' (Size = 50), @p191='?' (Size = 100), @p192='?' (DbType = DateTime2), @p193='?' (DbType = Boolean), @p194='?' (Size = 100), @p195='?' (Size = 50), @p196='?' (Size = 100), @p197='?' (DbType = DateTime2), @p198='?' (DbType = Boolean), @p199='?' (Size = 100), @p200='?' (Size = 50), @p201='?' (Size = 100), @p202='?' (DbType = DateTime2), @p203='?' (DbType = Boolean), @p204='?' (Size = 100), @p205='?' (Size = 50), @p206='?' (Size = 100), @p207='?' (DbType = DateTime2), @p208='?' (DbType = Boolean), @p209='?' (Size = 100)], CommandType='Text', CommandTimeout='30']
      SET IMPLICIT_TRANSACTIONS OFF;
      SET NOCOUNT ON;
      MERGE [Employees] USING (
      VALUES (@p0, @p1, @p2, @p3, @p4, 0),
      (@p5, @p6, @p7, @p8, @p9, 1),
      (@p10, @p11, @p12, @p13, @p14, 2),
      (@p15, @p16, @p17, @p18, @p19, 3),
      (@p20, @p21, @p22, @p23, @p24, 4),
      (@p25, @p26, @p27, @p28, @p29, 5),
      (@p30, @p31, @p32, @p33, @p34, 6),
      (@p35, @p36, @p37, @p38, @p39, 7),
      (@p40, @p41, @p42, @p43, @p44, 8),
      (@p45, @p46, @p47, @p48, @p49, 9),
      (@p50, @p51, @p52, @p53, @p54, 10),
      (@p55, @p56, @p57, @p58, @p59, 11),
      (@p60, @p61, @p62, @p63, @p64, 12),
      (@p65, @p66, @p67, @p68, @p69, 13),
      (@p70, @p71, @p72, @p73, @p74, 14),
      (@p75, @p76, @p77, @p78, @p79, 15),
      (@p80, @p81, @p82, @p83, @p84, 16),
      (@p85, @p86, @p87, @p88, @p89, 17),
      (@p90, @p91, @p92, @p93, @p94, 18),
      (@p95, @p96, @p97, @p98, @p99, 19),
      (@p100, @p101, @p102, @p103, @p104, 20),
      (@p105, @p106, @p107, @p108, @p109, 21),
      (@p110, @p111, @p112, @p113, @p114, 22),
      (@p115, @p116, @p117, @p118, @p119, 23),
      (@p120, @p121, @p122, @p123, @p124, 24),
      (@p125, @p126, @p127, @p128, @p129, 25),
      (@p130, @p131, @p132, @p133, @p134, 26),
      (@p135, @p136, @p137, @p138, @p139, 27),
      (@p140, @p141, @p142, @p143, @p144, 28),
      (@p145, @p146, @p147, @p148, @p149, 29),
      (@p150, @p151, @p152, @p153, @p154, 30),
      (@p155, @p156, @p157, @p158, @p159, 31),
      (@p160, @p161, @p162, @p163, @p164, 32),
      (@p165, @p166, @p167, @p168, @p169, 33),
      (@p170, @p171, @p172, @p173, @p174, 34),
      (@p175, @p176, @p177, @p178, @p179, 35),
      (@p180, @p181, @p182, @p183, @p184, 36),
      (@p185, @p186, @p187, @p188, @p189, 37),
      (@p190, @p191, @p192, @p193, @p194, 38),
      (@p195, @p196, @p197, @p198, @p199, 39),
      (@p200, @p201, @p202, @p203, @p204, 40),
      (@p205, @p206, @p207, @p208, @p209, 41)) AS i ([Department], [FirstName], [HireDate], [IsActive], [LastName], _Position) ON 1=0
      WHEN NOT MATCHED THEN
      INSERT ([Department], [FirstName], [HireDate], [IsActive], [LastName])
      VALUES (i.[Department], i.[FirstName], i.[HireDate], i.[IsActive], i.[LastName])
      OUTPUT INSERTED.[Id], i._Position;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (2ms) [Parameters=[@p0='?' (Size = 50), @p1='?' (Size = 100), @p2='?' (DbType = DateTime2), @p3='?' (DbType = Boolean), @p4='?' (Size = 100), @p5='?' (Size = 50), @p6='?' (Size = 100), @p7='?' (DbType = DateTime2), @p8='?' (DbType = Boolean), @p9='?' (Size = 100), @p10='?' (Size = 50), @p11='?' (Size = 100), @p12='?' (DbType = DateTime2), @p13='?' (DbType = Boolean), @p14='?' (Size = 100), @p15='?' (Size = 50), @p16='?' (Size = 100), @p17='?' (DbType = DateTime2), @p18='?' (DbType = Boolean), @p19='?' (Size = 100), @p20='?' (Size = 50), @p21='?' (Size = 100), @p22='?' (DbType = DateTime2), @p23='?' (DbType = Boolean), @p24='?' (Size = 100), @p25='?' (Size = 50), @p26='?' (Size = 100), @p27='?' (DbType = DateTime2), @p28='?' (DbType = Boolean), @p29='?' (Size = 100), @p30='?' (Size = 50), @p31='?' (Size = 100), @p32='?' (DbType = DateTime2), @p33='?' (DbType = Boolean), @p34='?' (Size = 100), @p35='?' (Size = 50), @p36='?' (Size = 100), @p37='?' (DbType = DateTime2), @p38='?' (DbType = Boolean), @p39='?' (Size = 100), @p40='?' (Size = 50), @p41='?' (Size = 100), @p42='?' (DbType = DateTime2), @p43='?' (DbType = Boolean), @p44='?' (Size = 100), @p45='?' (Size = 50), @p46='?' (Size = 100), @p47='?' (DbType = DateTime2), @p48='?' (DbType = Boolean), @p49='?' (Size = 100), @p50='?' (Size = 50), @p51='?' (Size = 100), @p52='?' (DbType = DateTime2), @p53='?' (DbType = Boolean), @p54='?' (Size = 100), @p55='?' (Size = 50), @p56='?' (Size = 100), @p57='?' (DbType = DateTime2), @p58='?' (DbType = Boolean), @p59='?' (Size = 100), @p60='?' (Size = 50), @p61='?' (Size = 100), @p62='?' (DbType = DateTime2), @p63='?' (DbType = Boolean), @p64='?' (Size = 100), @p65='?' (Size = 50), @p66='?' (Size = 100), @p67='?' (DbType = DateTime2), @p68='?' (DbType = Boolean), @p69='?' (Size = 100), @p70='?' (Size = 50), @p71='?' (Size = 100), @p72='?' (DbType = DateTime2), @p73='?' (DbType = Boolean), @p74='?' (Size = 100), @p75='?' (Size = 50), @p76='?' (Size = 100), @p77='?' (DbType = DateTime2), @p78='?' (DbType = Boolean), @p79='?' (Size = 100), @p80='?' (Size = 50), @p81='?' (Size = 100), @p82='?' (DbType = DateTime2), @p83='?' (DbType = Boolean), @p84='?' (Size = 100), @p85='?' (Size = 50), @p86='?' (Size = 100), @p87='?' (DbType = DateTime2), @p88='?' (DbType = Boolean), @p89='?' (Size = 100), @p90='?' (Size = 50), @p91='?' (Size = 100), @p92='?' (DbType = DateTime2), @p93='?' (DbType = Boolean), @p94='?' (Size = 100), @p95='?' (Size = 50), @p96='?' (Size = 100), @p97='?' (DbType = DateTime2), @p98='?' (DbType = Boolean), @p99='?' (Size = 100), @p100='?' (Size = 50), @p101='?' (Size = 100), @p102='?' (DbType = DateTime2), @p103='?' (DbType = Boolean), @p104='?' (Size = 100), @p105='?' (Size = 50), @p106='?' (Size = 100), @p107='?' (DbType = DateTime2), @p108='?' (DbType = Boolean), @p109='?' (Size = 100), @p110='?' (Size = 50), @p111='?' (Size = 100), @p112='?' (DbType = DateTime2), @p113='?' (DbType = Boolean), @p114='?' (Size = 100), @p115='?' (Size = 50), @p116='?' (Size = 100), @p117='?' (DbType = DateTime2), @p118='?' (DbType = Boolean), @p119='?' (Size = 100), @p120='?' (Size = 50), @p121='?' (Size = 100), @p122='?' (DbType = DateTime2), @p123='?' (DbType = Boolean), @p124='?' (Size = 100), @p125='?' (Size = 50), @p126='?' (Size = 100), @p127='?' (DbType = DateTime2), @p128='?' (DbType = Boolean), @p129='?' (Size = 100), @p130='?' (Size = 50), @p131='?' (Size = 100), @p132='?' (DbType = DateTime2), @p133='?' (DbType = Boolean), @p134='?' (Size = 100), @p135='?' (Size = 50), @p136='?' (Size = 100), @p137='?' (DbType = DateTime2), @p138='?' (DbType = Boolean), @p139='?' (Size = 100), @p140='?' (Size = 50), @p141='?' (Size = 100), @p142='?' (DbType = DateTime2), @p143='?' (DbType = Boolean), @p144='?' (Size = 100), @p145='?' (Size = 50), @p146='?' (Size = 100), @p147='?' (DbType = DateTime2), @p148='?' (DbType = Boolean), @p149='?' (Size = 100), @p150='?' (Size = 50), @p151='?' (Size = 100), @p152='?' (DbType = DateTime2), @p153='?' (DbType = Boolean), @p154='?' (Size = 100), @p155='?' (Size = 50), @p156='?' (Size = 100), @p157='?' (DbType = DateTime2), @p158='?' (DbType = Boolean), @p159='?' (Size = 100), @p160='?' (Size = 50), @p161='?' (Size = 100), @p162='?' (DbType = DateTime2), @p163='?' (DbType = Boolean), @p164='?' (Size = 100), @p165='?' (Size = 50), @p166='?' (Size = 100), @p167='?' (DbType = DateTime2), @p168='?' (DbType = Boolean), @p169='?' (Size = 100), @p170='?' (Size = 50), @p171='?' (Size = 100), @p172='?' (DbType = DateTime2), @p173='?' (DbType = Boolean), @p174='?' (Size = 100), @p175='?' (Size = 50), @p176='?' (Size = 100), @p177='?' (DbType = DateTime2), @p178='?' (DbType = Boolean), @p179='?' (Size = 100), @p180='?' (Size = 50), @p181='?' (Size = 100), @p182='?' (DbType = DateTime2), @p183='?' (DbType = Boolean), @p184='?' (Size = 100), @p185='?' (Size = 50), @p186='?' (Size = 100), @p187='?' (DbType = DateTime2), @p188='?' (DbType = Boolean), @p189='?' (Size = 100), @p190='?' (Size = 50), @p191='?' (Size = 100), @p192='?' (DbType = DateTime2), @p193='?' (DbType = Boolean), @p194='?' (Size = 100), @p195='?' (Size = 50), @p196='?' (Size = 100), @p197='?' (DbType = DateTime2), @p198='?' (DbType = Boolean), @p199='?' (Size = 100), @p200='?' (Size = 50), @p201='?' (Size = 100), @p202='?' (DbType = DateTime2), @p203='?' (DbType = Boolean), @p204='?' (Size = 100), @p205='?' (Size = 50), @p206='?' (Size = 100), @p207='?' (DbType = DateTime2), @p208='?' (DbType = Boolean), @p209='?' (Size = 100)], CommandType='Text', CommandTimeout='30']
      SET IMPLICIT_TRANSACTIONS OFF;
      SET NOCOUNT ON;
      MERGE [Employees] USING (
      VALUES (@p0, @p1, @p2, @p3, @p4, 0),
      (@p5, @p6, @p7, @p8, @p9, 1),
      (@p10, @p11, @p12, @p13, @p14, 2),
      (@p15, @p16, @p17, @p18, @p19, 3),
      (@p20, @p21, @p22, @p23, @p24, 4),
      (@p25, @p26, @p27, @p28, @p29, 5),
      (@p30, @p31, @p32, @p33, @p34, 6),
      (@p35, @p36, @p37, @p38, @p39, 7),
      (@p40, @p41, @p42, @p43, @p44, 8),
      (@p45, @p46, @p47, @p48, @p49, 9),
      (@p50, @p51, @p52, @p53, @p54, 10),
      (@p55, @p56, @p57, @p58, @p59, 11),
      (@p60, @p61, @p62, @p63, @p64, 12),
      (@p65, @p66, @p67, @p68, @p69, 13),
      (@p70, @p71, @p72, @p73, @p74, 14),
      (@p75, @p76, @p77, @p78, @p79, 15),
      (@p80, @p81, @p82, @p83, @p84, 16),
      (@p85, @p86, @p87, @p88, @p89, 17),
      (@p90, @p91, @p92, @p93, @p94, 18),
      (@p95, @p96, @p97, @p98, @p99, 19),
      (@p100, @p101, @p102, @p103, @p104, 20),
      (@p105, @p106, @p107, @p108, @p109, 21),
      (@p110, @p111, @p112, @p113, @p114, 22),
      (@p115, @p116, @p117, @p118, @p119, 23),
      (@p120, @p121, @p122, @p123, @p124, 24),
      (@p125, @p126, @p127, @p128, @p129, 25),
      (@p130, @p131, @p132, @p133, @p134, 26),
      (@p135, @p136, @p137, @p138, @p139, 27),
      (@p140, @p141, @p142, @p143, @p144, 28),
      (@p145, @p146, @p147, @p148, @p149, 29),
      (@p150, @p151, @p152, @p153, @p154, 30),
      (@p155, @p156, @p157, @p158, @p159, 31),
      (@p160, @p161, @p162, @p163, @p164, 32),
      (@p165, @p166, @p167, @p168, @p169, 33),
      (@p170, @p171, @p172, @p173, @p174, 34),
      (@p175, @p176, @p177, @p178, @p179, 35),
      (@p180, @p181, @p182, @p183, @p184, 36),
      (@p185, @p186, @p187, @p188, @p189, 37),
      (@p190, @p191, @p192, @p193, @p194, 38),
      (@p195, @p196, @p197, @p198, @p199, 39),
      (@p200, @p201, @p202, @p203, @p204, 40),
      (@p205, @p206, @p207, @p208, @p209, 41)) AS i ([Department], [FirstName], [HireDate], [IsActive], [LastName], _Position) ON 1=0
      WHEN NOT MATCHED THEN
      INSERT ([Department], [FirstName], [HireDate], [IsActive], [LastName])
      VALUES (i.[Department], i.[FirstName], i.[HireDate], i.[IsActive], i.[LastName])
      OUTPUT INSERTED.[Id], i._Position;
[06:29:41 INF] Executed DbCommand (2ms) [Parameters=[@p0='HR' (Nullable = false) (Size = 50), @p1='FirstName56043' (Nullable = false) (Size = 100), @p2='2017-10-04T06:29:39.1487602+02:00', @p3='True', @p4='LastName56043' (Nullable = false) (Size = 100), @p5='Sales' (Nullable = false) (Size = 50), @p6='FirstName56044' (Nullable = false) (Size = 100), @p7='2015-04-27T06:29:39.1487603+02:00', @p8='False', @p9='LastName56044' (Nullable = false) (Size = 100), @p10='HR' (Nullable = false) (Size = 50), @p11='FirstName56045' (Nullable = false) (Size = 100), @p12='2016-02-16T06:29:39.1487604+01:00', @p13='False', @p14='LastName56045' (Nullable = false) (Size = 100), @p15='Marketing' (Nullable = false) (Size = 50), @p16='FirstName56046' (Nullable = false) (Size = 100), @p17='2018-03-09T06:29:39.1487605+01:00', @p18='True', @p19='LastName56046' (Nullable = false) (Size = 100), @p20='Finance' (Nullable = false) (Size = 50), @p21='FirstName56047' (Nullable = false) (Size = 100), @p22='2023-02-23T06:29:39.1487606+01:00', @p23='False', @p24='LastName56047' (Nullable = false) (Size = 100), @p25='IT' (Nullable = false) (Size = 50), @p26='FirstName56048' (Nullable = false) (Size = 100), @p27='2021-01-20T06:29:39.1487607+01:00', @p28='False', @p29='LastName56048' (Nullable = false) (Size = 100), @p30='Finance' (Nullable = false) (Size = 50), @p31='FirstName56049' (Nullable = false) (Size = 100), @p32='2015-12-17T06:29:39.1487608+01:00', @p33='False', @p34='LastName56049' (Nullable = false) (Size = 100), @p35='Finance' (Nullable = false) (Size = 50), @p36='FirstName56050' (Nullable = false) (Size = 100), @p37='2016-05-03T06:29:39.1487609+02:00', @p38='True', @p39='LastName56050' (Nullable = false) (Size = 100), @p40='Marketing' (Nullable = false) (Size = 50), @p41='FirstName56051' (Nullable = false) (Size = 100), @p42='2019-08-11T06:29:39.1487610+02:00', @p43='True', @p44='LastName56051' (Nullable = false) (Size = 100), @p45='Sales' (Nullable = false) (Size = 50), @p46='FirstName56052' (Nullable = false) (Size = 100), @p47='2016-11-23T06:29:39.1487611+01:00', @p48='True', @p49='LastName56052' (Nullable = false) (Size = 100), @p50='Marketing' (Nullable = false) (Size = 50), @p51='FirstName56053' (Nullable = false) (Size = 100), @p52='2021-08-15T06:29:39.1487612+02:00', @p53='False', @p54='LastName56053' (Nullable = false) (Size = 100), @p55='Marketing' (Nullable = false) (Size = 50), @p56='FirstName56054' (Nullable = false) (Size = 100), @p57='2022-04-15T06:29:39.1487613+02:00', @p58='False', @p59='LastName56054' (Nullable = false) (Size = 100), @p60='Sales' (Nullable = false) (Size = 50), @p61='FirstName56055' (Nullable = false) (Size = 100), @p62='2015-08-05T06:29:39.1487614+02:00', @p63='True', @p64='LastName56055' (Nullable = false) (Size = 100), @p65='Marketing' (Nullable = false) (Size = 50), @p66='FirstName56056' (Nullable = false) (Size = 100), @p67='2021-07-20T06:29:39.1487615+02:00', @p68='False', @p69='LastName56056' (Nullable = false) (Size = 100), @p70='Finance' (Nullable = false) (Size = 50), @p71='FirstName56057' (Nullable = false) (Size = 100), @p72='2016-03-19T06:29:39.1487616+01:00', @p73='True', @p74='LastName56057' (Nullable = false) (Size = 100), @p75='IT' (Nullable = false) (Size = 50), @p76='FirstName56058' (Nullable = false) (Size = 100), @p77='2017-02-23T06:29:39.1487617+01:00', @p78='False', @p79='LastName56058' (Nullable = false) (Size = 100), @p80='IT' (Nullable = false) (Size = 50), @p81='FirstName56059' (Nullable = false) (Size = 100), @p82='2016-12-30T06:29:39.1487618+01:00', @p83='True', @p84='LastName56059' (Nullable = false) (Size = 100), @p85='Sales' (Nullable = false) (Size = 50), @p86='FirstName56060' (Nullable = false) (Size = 100), @p87='2018-04-26T06:29:39.1487619+02:00', @p88='False', @p89='LastName56060' (Nullable = false) (Size = 100), @p90='HR' (Nullable = false) (Size = 50), @p91='FirstName56061' (Nullable = false) (Size = 100), @p92='2015-08-03T06:29:39.1487620+02:00', @p93='True', @p94='LastName56061' (Nullable = false) (Size = 100), @p95='Marketing' (Nullable = false) (Size = 50), @p96='FirstName56062' (Nullable = false) (Size = 100), @p97='2021-08-16T06:29:39.1487621+02:00', @p98='False', @p99='LastName56062' (Nullable = false) (Size = 100), @p100='HR' (Nullable = false) (Size = 50), @p101='FirstName56063' (Nullable = false) (Size = 100), @p102='2024-03-21T06:29:39.1487622+01:00', @p103='False', @p104='LastName56063' (Nullable = false) (Size = 100), @p105='Sales' (Nullable = false) (Size = 50), @p106='FirstName56064' (Nullable = false) (Size = 100), @p107='2015-09-30T06:29:39.1487623+02:00', @p108='False', @p109='LastName56064' (Nullable = false) (Size = 100), @p110='Finance' (Nullable = false) (Size = 50), @p111='FirstName56065' (Nullable = false) (Size = 100), @p112='2022-12-18T06:29:39.1487624+01:00', @p113='False', @p114='LastName56065' (Nullable = false) (Size = 100), @p115='IT' (Nullable = false) (Size = 50), @p116='FirstName56066' (Nullable = false) (Size = 100), @p117='2017-05-25T06:29:39.1487625+02:00', @p118='True', @p119='LastName56066' (Nullable = false) (Size = 100), @p120='Marketing' (Nullable = false) (Size = 50), @p121='FirstName56067' (Nullable = false) (Size = 100), @p122='2019-10-26T06:29:39.1487629+02:00', @p123='False', @p124='LastName56067' (Nullable = false) (Size = 100), @p125='HR' (Nullable = false) (Size = 50), @p126='FirstName56068' (Nullable = false) (Size = 100), @p127='2016-04-11T06:29:39.1487630+02:00', @p128='True', @p129='LastName56068' (Nullable = false) (Size = 100), @p130='Marketing' (Nullable = false) (Size = 50), @p131='FirstName56069' (Nullable = false) (Size = 100), @p132='2017-08-02T06:29:39.1487631+02:00', @p133='False', @p134='LastName56069' (Nullable = false) (Size = 100), @p135='IT' (Nullable = false) (Size = 50), @p136='FirstName56070' (Nullable = false) (Size = 100), @p137='2022-10-08T06:29:39.1487632+02:00', @p138='False', @p139='LastName56070' (Nullable = false) (Size = 100), @p140='IT' (Nullable = false) (Size = 50), @p141='FirstName56071' (Nullable = false) (Size = 100), @p142='2019-05-03T06:29:39.1487633+02:00', @p143='False', @p144='LastName56071' (Nullable = false) (Size = 100), @p145='HR' (Nullable = false) (Size = 50), @p146='FirstName56072' (Nullable = false) (Size = 100), @p147='2015-06-14T06:29:39.1487634+02:00', @p148='True', @p149='LastName56072' (Nullable = false) (Size = 100), @p150='Marketing' (Nullable = false) (Size = 50), @p151='FirstName56073' (Nullable = false) (Size = 100), @p152='2019-02-23T06:29:39.1487635+01:00', @p153='False', @p154='LastName56073' (Nullable = false) (Size = 100), @p155='Finance' (Nullable = false) (Size = 50), @p156='FirstName56074' (Nullable = false) (Size = 100), @p157='2017-02-28T06:29:39.1487636+01:00', @p158='False', @p159='LastName56074' (Nullable = false) (Size = 100), @p160='Finance' (Nullable = false) (Size = 50), @p161='FirstName56075' (Nullable = false) (Size = 100), @p162='2017-04-02T06:29:39.1487637+02:00', @p163='False', @p164='LastName56075' (Nullable = false) (Size = 100), @p165='HR' (Nullable = false) (Size = 50), @p166='FirstName56076' (Nullable = false) (Size = 100), @p167='2020-10-25T06:29:39.1487638+01:00', @p168='False', @p169='LastName56076' (Nullable = false) (Size = 100), @p170='HR' (Nullable = false) (Size = 50), @p171='FirstName56077' (Nullable = false) (Size = 100), @p172='2024-01-15T06:29:39.1487639+01:00', @p173='True', @p174='LastName56077' (Nullable = false) (Size = 100), @p175='IT' (Nullable = false) (Size = 50), @p176='FirstName56078' (Nullable = false) (Size = 100), @p177='2019-09-17T06:29:39.1487640+02:00', @p178='False', @p179='LastName56078' (Nullable = false) (Size = 100), @p180='Marketing' (Nullable = false) (Size = 50), @p181='FirstName56079' (Nullable = false) (Size = 100), @p182='2021-12-15T06:29:39.1487641+01:00', @p183='True', @p184='LastName56079' (Nullable = false) (Size = 100), @p185='Finance' (Nullable = false) (Size = 50), @p186='FirstName56080' (Nullable = false) (Size = 100), @p187='2018-12-28T06:29:39.1487642+01:00', @p188='True', @p189='LastName56080' (Nullable = false) (Size = 100), @p190='Marketing' (Nullable = false) (Size = 50), @p191='FirstName56081' (Nullable = false) (Size = 100), @p192='2020-07-13T06:29:39.1487643+02:00', @p193='False', @p194='LastName56081' (Nullable = false) (Size = 100), @p195='Marketing' (Nullable = false) (Size = 50), @p196='FirstName56082' (Nullable = false) (Size = 100), @p197='2023-04-28T06:29:39.1487644+02:00', @p198='False', @p199='LastName56082' (Nullable = false) (Size = 100), @p200='Sales' (Nullable = false) (Size = 50), @p201='FirstName56083' (Nullable = false) (Size = 100), @p202='2023-04-17T06:29:39.1487645+02:00', @p203='False', @p204='LastName56083' (Nullable = false) (Size = 100), @p205='Sales' (Nullable = false) (Size = 50), @p206='FirstName56084' (Nullable = false) (Size = 100), @p207='2017-03-18T06:29:39.1487646+01:00', @p208='False', @p209='LastName56084' (Nullable = false) (Size = 100)], CommandType='Text', CommandTimeout='30']
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
MERGE [Employees] USING (
VALUES (@p0, @p1, @p2, @p3, @p4, 0),
(@p5, @p6, @p7, @p8, @p9, 1),
(@p10, @p11, @p12, @p13, @p14, 2),
(@p15, @p16, @p17, @p18, @p19, 3),
(@p20, @p21, @p22, @p23, @p24, 4),
(@p25, @p26, @p27, @p28, @p29, 5),
(@p30, @p31, @p32, @p33, @p34, 6),
(@p35, @p36, @p37, @p38, @p39, 7),
(@p40, @p41, @p42, @p43, @p44, 8),
(@p45, @p46, @p47, @p48, @p49, 9),
(@p50, @p51, @p52, @p53, @p54, 10),
(@p55, @p56, @p57, @p58, @p59, 11),
(@p60, @p61, @p62, @p63, @p64, 12),
(@p65, @p66, @p67, @p68, @p69, 13),
(@p70, @p71, @p72, @p73, @p74, 14),
(@p75, @p76, @p77, @p78, @p79, 15),
(@p80, @p81, @p82, @p83, @p84, 16),
(@p85, @p86, @p87, @p88, @p89, 17),
(@p90, @p91, @p92, @p93, @p94, 18),
(@p95, @p96, @p97, @p98, @p99, 19),
(@p100, @p101, @p102, @p103, @p104, 20),
(@p105, @p106, @p107, @p108, @p109, 21),
(@p110, @p111, @p112, @p113, @p114, 22),
(@p115, @p116, @p117, @p118, @p119, 23),
(@p120, @p121, @p122, @p123, @p124, 24),
(@p125, @p126, @p127, @p128, @p129, 25),
(@p130, @p131, @p132, @p133, @p134, 26),
(@p135, @p136, @p137, @p138, @p139, 27),
(@p140, @p141, @p142, @p143, @p144, 28),
(@p145, @p146, @p147, @p148, @p149, 29),
(@p150, @p151, @p152, @p153, @p154, 30),
(@p155, @p156, @p157, @p158, @p159, 31),
(@p160, @p161, @p162, @p163, @p164, 32),
(@p165, @p166, @p167, @p168, @p169, 33),
(@p170, @p171, @p172, @p173, @p174, 34),
(@p175, @p176, @p177, @p178, @p179, 35),
(@p180, @p181, @p182, @p183, @p184, 36),
(@p185, @p186, @p187, @p188, @p189, 37),
(@p190, @p191, @p192, @p193, @p194, 38),
(@p195, @p196, @p197, @p198, @p199, 39),
(@p200, @p201, @p202, @p203, @p204, 40),
(@p205, @p206, @p207, @p208, @p209, 41)) AS i ([Department], [FirstName], [HireDate], [IsActive], [LastName], _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT ([Department], [FirstName], [HireDate], [IsActive], [LastName])
VALUES (i.[Department], i.[FirstName], i.[HireDate], i.[IsActive], i.[LastName])
OUTPUT INSERTED.[Id], i._Position;

Include provider and version information

EF Core version: 8.0.11
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 8.0
Operating system: Windows 11, Windows Server 2019

@roji
Copy link
Member

roji commented Dec 22, 2024

Your sample includes lots of components, such as GraphQL, ASP.NET, etc. To relliably benchmark and isolate EF specifically, please extract the EF code into a minimal BenchmarkDotNet benchmark, using the [MemoryDiagnoer] to get memory results. At that point, if you still see unreasonable memory usage, please post the benchmark along with the results and we'll investigate.

@roji
Copy link
Member

roji commented Dec 22, 2024

BTW I'm noticing that you're instantiating 100k objects for the seeding, that in itself will already use up quite a lot of memory, which the GC won't necesary recollect right away (if it doesn't need to).

I'd advise using a memory profiler to see what the actual objects are which take up memory. You can also extract out the seeding to an external program, to isolate that as well.

@Meteoeoeo
Copy link
Author

I'm just getting familiar with BenchmarkDotNet, and I'm not quite sure what to use to indicate that something remains in memory after disposal.

Perhaps the following code will be helpful:

        long memoryBeforeInsert = GC.GetTotalMemory(forceFullCollection: false);

        using (var context = CreateDbContext())
        {
            EmployeeSeeder.Seed(context);
        }

        long memoryAfterInsert = GC.GetTotalMemory(forceFullCollection: false);

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        long memoryAfterDispose = GC.GetTotalMemory(forceFullCollection: false);

        Console.WriteLine($"Memory Before Insert: {memoryBeforeInsert} bytes");
        Console.WriteLine($"Memory After Insert: {memoryAfterInsert} bytes");
        Console.WriteLine($"Memory After Dispose: {memoryAfterDispose} bytes");

        memoryBeforeInsert = GC.GetTotalMemory(forceFullCollection: false);

        using (var context = CreateDbContext())
        {
            EmployeeSeeder.Seed(context);
        }

        memoryAfterInsert = GC.GetTotalMemory(forceFullCollection: false);

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        memoryAfterDispose = GC.GetTotalMemory(forceFullCollection: false);

        Console.WriteLine($"Memory Before Insert: {memoryBeforeInsert} bytes");
        Console.WriteLine($"Memory After Insert: {memoryAfterInsert} bytes");
        Console.WriteLine($"Memory After Dispose: {memoryAfterDispose} bytes");

Output:

Memory Before Insert: 1613968 bytes
Memory After Insert: 31161072 bytes
Memory After Dispose: 3394120 bytes
Memory Before Insert: 3410568 bytes
Memory After Insert: 32869256 bytes
Memory After Dispose: 5119288 bytes

When I Use memoryDiagnoser

[MemoryDiagnoser]
public class EfCoreMemoryUsageBenchmark
{
    private const int RecordCount = 1000;

    [Benchmark]
    public void WithSaveChanges()
    {
        using (var context = CreateDbContext())
        {
            EmployeeSeeder.Seed(context);
        }
        return;
    }

    [Benchmark]
    public void WithoutSaveChanges()
    {
        using (var context = CreateDbContext())
        {
            EmployeeSeeder.SeedWithoutSaveChanges(context);
        }
        return;
    }

    private EmployeeContext CreateDbContext()
    {
        var options = new DbContextOptionsBuilder<EmployeeContext>()
            .UseSqlServer("Server=localhost;Database=EmployeesDatabase;Trusted_Connection=True;TrustServerCertificate=True;")
            .Options;
        return new EmployeeContext(options);
    }
}

I got:

Method Mean Error StdDev Gen0 Gen1 Gen2 Allocated
WithSaveChanges 4,321.2 ms 20,330.56 ms 1,114.39 ms 46000.0000 30000.0000 1000.0000 819.31 MB
WithoutSaveChanges 149.9 ms 41.32 ms 2.26 ms 6750.0000 4750.0000 1750.0000 113.06 MB

    public static class EmployeeSeeder
    {
        public static void Seed(EmployeeContext context)
        {
            var random = new Random();
            var departments = new[] { "IT", "HR", "Finance", "Marketing", "Sales" };

            var employees = Enumerable.Range(1, 100_000).Select(i => new Employee
            {
                FirstName = $"FirstName{i}",
                LastName = $"LastName{i}",
                Department = departments[random.Next(departments.Length)],
                IsActive = random.Next(0, 2) == 1,
                HireDate = DateTime.Now.AddDays(-random.Next(0, 3650))
            }).ToList();

            var batchSize = 1_000;
            for (int i = 0; i < employees.Count; i += batchSize)
            {
                var batch = employees.Skip(i).Take(batchSize).ToList();
                context.Employees.AddRange(batch);
                context.SaveChanges();
                context.ChangeTracker.Clear();
            }
        }

        public static void SeedWithoutSaveChanges(EmployeeContext context)
        {
            var random = new Random();
            var departments = new[] { "IT", "HR", "Finance", "Marketing", "Sales" };

            var employees = Enumerable.Range(1, 100_000).Select(i => new Employee
            {
                FirstName = $"FirstName{i}",
                LastName = $"LastName{i}",
                Department = departments[random.Next(departments.Length)],
                IsActive = random.Next(0, 2) == 1,
                HireDate = DateTime.Now.AddDays(-random.Next(0, 3650))
            }).ToList();

            var batchSize = 1_000;
            for (int i = 0; i < employees.Count; i += batchSize)
            {
                var batch = employees.Skip(i).Take(batchSize).ToList();
                context.Employees.AddRange(batch);
            }
        }
    }

@roji
Copy link
Member

roji commented Dec 24, 2024

BenchmarkDotNet's [MemoryDiagnoser] tells you how much memory was allocated for each iteration, not whether that memory still stays referenced (i.e. a memory leak). To know which objects are still referenced (and so detect a possible memory leak), you can use a memory profiler such as the one included inside VS, DotMemory, or similar.

To be sure I understand, what is it that you think you're seeing here, unreasonable memory consumption, a memory leak, or otherwise? It's a bit difficult to tell.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants