EFCore使用静态DbContext导致EFCore Tracking对象不能释放

背景

在排查内存异常的时候,发现一个对象的数量在经历导入——更新——删除之后数量在一直增加,如下图所示,经过2600多次导入后Case的引用数量在托管内存里也达到了2600多次。这肯定是不对的……

EFCore使用静态DbContext导致EFCore Tracking对象不能释放_第1张图片

 

产生原因

通过查询MSDN的相关资料后发现,EFCore的DbContext在进行更新时是会生成一个Tracking对象,用于对更新对象进行跟踪。也就是说若使用静态的DbContext,那么DbContext在更新数据时产生的Tracking对象是没有办法进行释放的,毕竟使用静态的DbContext是不可能将DbContext Dispose掉的,若不手动清除的话。

那么如下的这种写法就是一种要命的写法:

public static readonly MyDbContext Instance = new();

 

 

可能导致的后果

由于Tracking对象不能被释放掉,相应对象所占空间在内存中会一直存在,可能导致程序不能正常运行。不过小子遇到的情况还好,目前2606次引用消耗的内存仅1314208bit,一次引用为504bit,即使连续运行1万次也才4.8M左右;考虑到除持有case的Tracking外,还有两个不持有case的Tracking对象,1万次运行总共对内存的耗损也就6M以上。

821c71fd1ef047bb81f9e7e789df384c.png

只是由于这些对象数量的增加,EFCore在操作Tracking时很可能会导致时间延长(此还有待验证)。

 

解决办法

  1. 不要使用静态的DbContext,每次都重新获取DbContext的新实例;以下为添加数据时的使用示例,详细参考MSDN基本 SaveChanges - EF Core | Microsoft Learn:
    using (var context = new BloggingContext())
    {
        var blog = new Blog { Url = "http://example.com" };
        context.Blogs.Add(blog);
        context.SaveChanges();
    }
  2. 使用局部的静态DbContext——本质就是规定使用多少次Dbcontext或检测产生了多少个Tracking对象后就将原来的DbContext释放,然后重新获取新的DbContext。这样可以避免每次都使用新实例时产生一定的性能消耗。
  3. 一定要使用静态的DbContext的话,更新/删除/插入数据后就将ChangeTracker清空。
    MyContext.ChangeTracker?.Clear();

    建议在使用的DbContext内触发SavedChanges事件,在事件内清空ChangeTracker.

 参考链接实体框架文档中心 | Microsoft Learn

 

你可能感兴趣的:(EFCore)