在我的测试中,我正在逐行读取文本文件并插入实体以及其他相关实体。问题是当插入太多时我收到内存不足的异常。
在我试图阻止这种情况时,我每50行创建一个新的DbContext并处理旧的DbContext。我的理解是,这将释放早期实体操作的内存,但内存继续爬升,如果文件足够大,则会发生内存不足异常。这与实体代码有关,好像我删除了添加内存保持一致使用的实体的代码行。
以下是我的代码的简化版本。
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();
}
}
}
当我每隔50个插入处理并创建一个新的上下文时,我希望内存使用率保持不变,但是对于前2千行似乎是不变的,但在此之后内存不断攀升,单元格OutOfMemory异常是击中。
是否有理由以下列方式处理dbContext不会导致内存被释放?
编辑 - 添加了一些我的添加实体方法的简化代码
public void AddMyEntity(string[] fields, MyDbContext, myDbContext)
{
MyEntity myEntity = new MyEntity();
newRequest.InsertDate = DateTime.UtcNow;
newRequest.AmendDate = DateTime.UtcNow;
//If I remove this line the memory does not consistently climb
myDbContext.MyEntities.Add(myEntity);
foreach(string item in fields)
{
ReleatedEntity releatedEntity = new ReleatedEntity();
releatedEntity.Value = item;
newRequest.ReleatedEntities.Add(releatedEntity);
}
}
另一个编辑
经过更多测试后,它与Glimpse Profiler有关。我已将Glimpse包含在我的项目中,并且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设置为Off可修复内存泄漏。仍然不确定为什么要
答案 0 :(得分:2)
在对象上调用Dispose
不一定会释放内存。当垃圾收集器不再被任何活动对象引用时,对象将从内存中删除。调用Dispose
可能会释放其他资源(例如,在DbContext
的情况下通过关闭打开的SQL连接),但只有当对象不再被引用它才成为候选者时才会这样做垃圾收集。
无法保证垃圾收集器将在特定时间点运行。调用Dispose
当然不会导致它运行。话虽如此,我很惊讶它在你的内存耗尽之前没有运行。您可以强制它与GC.Collect
一起运行,但这确实不是必需的。
您可能仍然引用了上下文对象,导致它们不被视为符合垃圾回收条件。例如,您将myDbContext
传递给服务层中的AddEntity
方法 - 是否会将包含反向引用(甚至间接)的内容存储到上下文中?
答案 1 :(得分:0)
因为无论何时调用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(fields);
中,您正在使用MyService
类中的DbContext,而不是使用line创建的DbContext。