我们看到内存资源没有被释放:
使用以下代码使用 .NET Core:
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
我按预期得到一条直线,谓词内存将无限期地继续上升。因此,虽然我可以解决问题,但我想了解发生了什么?
编辑:
将行更改为:
public void RunTest() {
using (var context = new EasyMwsContext()) {
var result = context.FeedSubmissionEntries.ToList();
}
}
给出图形:
所以我也不相信这是由于客户端评估造成的?
编辑2:
使用 EF 核心 2.1.4
和对象堆:
编辑3:
添加了保留图,似乎是 EF Core 的问题?
最佳答案
我怀疑罪魁祸首不是内存泄漏,而是对 EF Core 的一个相当不幸的补充,Client Evaluation .与 LINQ-to-SQL 一样,当遇到无法转换为 SQL 的 lambda/函数时,EF Core 将创建一个更简单的查询,该查询读取更多数据并在客户端评估该函数。
在您的情况下,EF Core 不知道是什么 TestPredicate
所以它会读取内存中的每条记录,然后尝试过滤数据。
顺便说一句 that's what happened当 SO 于 2018 年 10 月 4 日上周四转移到 EF Core 时。查询返回的不是几十行,而是...... 5200 万行:
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));
}
更新
找出泄漏的快速方法是在“诊断”窗口中拍摄两个内存快照并检查创建了哪些新对象以及它们使用了多少内存。客户端评估中很可能存在错误。
关于C# Entity Framework IQueryable 内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52735827/