为什么处理实体上下文不会释放内存

时间:2015-12-08 16:00:44

标签: c# asp.net entity-framework memory glimpse

在我的测试中,我正在逐行读取文本文件并插入实体以及其他相关实体。问题是当插入太多时我收到内存不足的异常。

在我试图阻止这种情况时,我每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可修复内存泄漏。仍然不确定为什么要

2 个答案:

答案 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。