C# 为什么处理实体上下文不能释放内存
在我的测试中,我逐行读取一个文本文件,并插入一个实体以及其他相关实体。问题是当插入太多时,我会收到内存不足异常 为了防止这种情况,我为每50行创建一个新的DbContext,并处理旧的DbContext。据我所知,这将从早期的实体操作中释放内存,但内存会继续增加,如果文件足够大,则会发生内存不足异常。这与实体代码有关,就像我删除了添加实体的代码行一样,内存保持一致的使用 下面是我的代码的简化版本C# 为什么处理实体上下文不能释放内存,c#,asp.net,entity-framework,memory,glimpse,C#,Asp.net,Entity Framework,Memory,Glimpse,在我的测试中,我逐行读取一个文本文件,并插入一个实体以及其他相关实体。问题是当插入太多时,我会收到内存不足异常 为了防止这种情况,我为每50行创建一个新的DbContext,并处理旧的DbContext。据我所知,这将从早期的实体操作中释放内存,但内存会继续增加,如果文件足够大,则会发生内存不足异常。这与实体代码有关,就像我删除了添加实体的代码行一样,内存保持一致的使用 下面是我的代码的简化版本 public class TestClass { public void ImportData
public class TestClass
{
public void ImportData(byte[] fileBytes)
{
using (Stream stream = new MemoryStream(fileBytes))
{
TextFieldParser parser = new TextFieldParser(stream);
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
while (!parser.EndOfData)
{
//Processes 50 lines creates a new DbContext each time its called
ImportBatch(parser);
}
}
}
public void ImportBatch(TextFieldParser parser)
{
using(myDbContext context = new myDbContext())
{
context.Configuration.AutoDetectChangesEnabled = false;
int batchCount = 0;
while (!parser.EndOfData && batchCount < 50)
{
string[] fields = parser.ReadFields();
//Here I call some code that will add an entity and add releated entities
//In its navigation properties
MyService.AddMyEntity(fields,myDbContext);
batchCount++;
}
myDbContext.ChangeTracker.DetectChanges();
myDbContext.SaveChanges();
}
}
}
另一次编辑
经过更多的测试后发现,这与Spiew profiler有关。我在我的项目中加入了一瞥,web配置有一个类似于下面的部分
<glimpse defaultRuntimePolicy="On" endpointBaseUri="~/Glimpse.axd">
<tabs>
<ignoredTypes>
<add type="Glimpse.Mvc.Tab.ModelBinding, Glimpse.Mvc5"/>
<add type="Glimpse.Mvc.Tab.Metadata, Glimpse.Mvc5"/>
</ignoredTypes>
</tabs>
<inspectors>
<ignoredTypes>
<add type="Glimpse.Mvc.Inspector.ModelBinderInspector, Glimpse.Mvc5"/>
</ignoredTypes>
</inspectors>
将defaultRuntimePolicy关闭可修复内存泄漏。仍然不知道为什么会这样,因为无论何时调用ImportBatch(解析器),它都会创建一个新的DbContext。不是每50个都有1个DbContext。您可以尝试使用get属性计数并将上下文返回给您。大概是这样的:
int _batchCount = 0;
public myDbContext _db;
public myDbContext Db
{
get
{
// If batchCount > 50 or _db is not created we need to create _db
if (_db == null || _batchCount > 50)
{
// If db is already created _batchcount > 50
if (_db != null)
{
_db.ChangeTracker.DetectChanges();
_db.SaveChanges();
_db.Dispose();
}
_db = new myDbContext();
_db.Configuration.AutoDetectChangesEnabled = false;
_batchCount = 0;
}
batchCount++;
return _db;
}
}
另外在
MyService.AddMyEntity(字段)中
您使用的是MyService
类中的DbContext,而不是在using line中创建的DbContext。对对象调用Dispose
,不一定会释放内存。当对象不再被任何活动对象引用时,垃圾收集器会将其从内存中删除。调用Dispose
可能会释放其他资源(例如,在DbContext
的情况下关闭打开的SQL连接),但只有当对象不再被引用时,它才会成为垃圾收集的候选对象
无法保证垃圾收集器将在特定时间点运行。调用Dispose
肯定不会导致它运行。话虽如此,我很惊讶它在内存耗尽之前不会运行。您可以强制它与GC.Collect一起运行,但这确实不是必需的
有可能您仍然有一个对上下文对象的引用,导致它们被认为不符合垃圾收集的条件。例如,您将myDbContext
传递给您的服务层中的AddEntity
方法-该方法是否存储了包含对上下文的反向引用(甚至是间接引用)的内容?您不是一直在添加前50个字段吗?@GregoryHouseMD您的权利,我的错误,将进行编辑。无论哪种方式,这都只是演示我的问题的简化代码,因为您正在处理一个对象并不意味着它正在被垃圾收集。您正在运行Visual Studio 2015吗?如果没有,请获取Community edition并在分析器运行时进行调试。您将看到垃圾收集活动。如果没有看到任何垃圾收集,则可能必须在ImportBatch方法末尾执行一些手动垃圾收集。可能MyService.AddMyEntity
会泄漏内存。你也可以发布这个方法的代码吗?为什么只在上下文后面创建另一个上下文而不处理上下文呢?这并不比原来的循环好——您刚刚将引用移动到一个类字段。实际上,dispose of context会创建另一个循环,因为它需要这样做。如果_batchCount>50,则每当调用较新的一个时,它都会创建另一个,并处理旧的。我想这就是我们在那里要做的。
int _batchCount = 0;
public myDbContext _db;
public myDbContext Db
{
get
{
// If batchCount > 50 or _db is not created we need to create _db
if (_db == null || _batchCount > 50)
{
// If db is already created _batchcount > 50
if (_db != null)
{
_db.ChangeTracker.DetectChanges();
_db.SaveChanges();
_db.Dispose();
}
_db = new myDbContext();
_db.Configuration.AutoDetectChangesEnabled = false;
_batchCount = 0;
}
batchCount++;
return _db;
}
}