C#EntityFramework IQueryable内存泄漏
我们看到内存资源无法释放: 使用.NET Core编写以下代码: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 {
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核心版本?这样的错误是版本特定的。是的,很抱歉,它在阻止客户端评估时也会出错,因此可以确认它肯定会这样做。编辑主票证。