C#EntityFramework IQueryable内存泄漏

C#EntityFramework IQueryable内存泄漏,c#,memory-leaks,.net-core,entity-framework-core,C#,Memory Leaks,.net Core,Entity Framework Core,我们看到内存资源无法释放: 使用.NET Core编写以下代码: class Program { static void Main(string[] args) { while (true) { var testRunner = new TestRunner(); testRunner.RunTest(); } } } public class TestRunner {

我们看到内存资源无法释放:

使用.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";
    }
}
如果我删除测试谓词
。其中
我得到了一条预期的直线,有了该谓词,内存将继续无限增长

因此,虽然我可以解决问题,但我想了解发生了什么

编辑:

将行更改为:

public void RunTest() {
    using (var context = new EasyMwsContext()) {
        var result = context.FeedSubmissionEntries.ToList();
    }
}
给出了图表:

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

编辑2:

使用EF Core 2.1.4

和对象堆:

编辑3:

添加了保留图,似乎是EF Core的问题


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

在您的例子中,EF Core无法知道什么是
TestPredicate
,因此它将读取内存中的每条记录,并尝试在之后过滤数据

顺便说一句,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日志记录,则该警告将无效

安全的解决方案是在每个上下文的
onconfigurang
方法中或在Startup.cs配置中全局禁用客户端评估,如文档部分所示:

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


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

我最后遇到了同样的问题。一旦我知道问题出在哪里,我就能够在EntityFrameworkCore存储库中找到它的bug报告

简短的总结是,当您在IQueryable中包含实例方法时,它会被缓存,并且即使在您的上下文被释放之后,这些方法也不会被释放

目前看来,在解决这一问题方面没有取得多大进展。我会密切关注,但目前我认为避免内存泄漏的最佳选择是:

  • 重写方法,使IQueryable中不包含任何实例方法
  • 在使用包含实例方法的LINQ方法之前,使用ToList()将iQueryTable转换为列表(如果试图限制数据库查询的结果,则不理想)
  • 将调用的方法设为static以限制内存的累积量

  • 假设你知道垃圾收集器是如何工作的,为什么你认为这是个问题?你是否已经运行到遇到问题的地步了?那些是什么,你有没有内存异常?IQueryable不会泄漏,因为它什么都不做。它表示尚未执行的查询。不过,代码本身创建了许多termporary对象<代码>其中(fse=>TestPredicate(fse))
    是可疑的-除非编译器内联
    TestPredicate
    ,否则无法将其转换为SQL。您可能正在将整个数据库加载到内存中进行筛选,但没有意识到这一点。1)您使用的是哪个EF Core版本2)在诊断窗口中拍摄两个内存快照,并检查哪些对象导致内存增加。3) EF核心。它是可选的,但默认为启用。使用
    .ConfigureWarnings(warnings=>warnings.Throw(RelationalEventId.QueryClientEvaluationWarnings))非常重要。2.2(预览版)之前的任何版本都被认为是错误的,没有真正的修复。见鬼,3.0之前的任何东西都不会得到任何主要的LINQ返工,而且这是一个经常失败的部分。即使在无限循环中创建临时字符串变量,最终也会出现内存异常。感谢您的回复,我编辑了主要问题-这是一个非常小的测试表,所以我不认为这是由于客户端评估造成的?@NickSpicer使用诊断窗口中的内存快照检查创建了哪些新对象。在任何情况下,都是EF核心加载数据,而不是IQueryable。您可能在客户评估中发现了真正的漏洞。您使用了哪个EF核心版本?这样的错误是版本特定的。是的,很抱歉,它在阻止客户端评估时也会出错,因此可以确认它肯定会这样做。编辑主票证。