C#EntityFramework IQueryable内存泄漏

时间:2018-10-10 08:28:02

标签: c# memory-leaks .net-core entity-framework-core

我们看到内存资源没有释放:

enter image description here

使用.NET Core使用以下代码:

Auto Layout

如果我删除测试谓词class Program { static void Main(string[] args) { while (true) { var testRunner = new TestRunner(); testRunner.RunTest(); } } } public class TestRunner { public void RunTest() { using (var context = new EasyMwsContext()) { var result = context.FeedSubmissionEntries.Where(fse => TestPredicate(fse)).ToList(); } } public bool TestPredicate(FeedSubmissionEntry e) { return e.AmazonRegion == AmazonRegion.Europe && e.MerchantId == "1234"; } } ,则会得到一条直线,如预期那样,有了该谓词,内存将无限期地继续增长。

因此,在我可以解决问题的同时,我想了解发生了什么事?

编辑:

将行更改为:

.Where

给出图形: enter image description here

所以我也不认为这是由于客户端评估造成的吗?

编辑2:

使用EF Core 2.1.4

和对象堆: enter image description here

编辑3:

添加了保留图,似乎与EF Core有关?

enter image description here

2 个答案:

答案 0 :(得分:2)

我怀疑罪魁祸首不是内存泄漏,而是EF Core Client Evaluation的不幸添加。与LINQ-to-SQL一样,当遇到无法转换为SQL的lambda /函数时,EF Core将创建一个更简单的查询,该查询读取更多数据并在客户端上评估该函数。 / p>

在您的情况下,EF Core无法知道TestPredicate是什么,因此它将读取内存中的每个记录,然后尝试过滤数据。

顺便说一句that's what happened,于SO于2018年10月4日星期四移至EF Core。该查询未返回几十行,而是返回了... 52百万行:

var answers = db.Posts
                .Where(p => grp.Select(g=>g.PostId).Contains(p.Id))
                ...
                .ToList();

客户端评估是可选的,但默认情况下处于启用状态。每次执行客户端评估时,EF Core都会记录一条警告,但是如果您没有配置EF Core日志记录,这将无济于事。

安全的解决方案是禁用客户端评估,如文档的Optional behavior: throw an exception for client evaluation部分所示,可以在每种上下文的OnConfiguring方法中使用,也可以在Startup中全局使用。 cs配置:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(...)
        .ConfigureWarnings(warnings => 
                           warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}

更新

找出泄漏的快速方法是在“诊断”窗口中拍摄两个内存快照,并检查创建了哪些新对象以及它们使用了多少内存。客户评估中很可能存在错误。

答案 1 :(得分:2)

我最终遇到了同样的问题。一旦知道了问题所在,便可以在EntityFrameworkCore存储库中here找到错误报告。

简短的摘要是,当您将实例方法包含在IQueryable中时,它将被缓存,并且即使在处理完上下文后也不会释放这些方法。

目前,在解决该问题上似乎还没有取得太大进展。我会一直关注它,但就目前而言,我认为避免内存泄漏的最佳选择是:

  1. 重写您的方法,以便IQueryable中不包含实例方法
  2. 在使用包含实例方法的LINQ方法之前,先使用ToList()将IQueryable转换为列表(如果要限制数据库查询的结果,则不理想)
  3. 将您要调用的方法设为静态,以限制堆积的内存量