【问题标题】:Why does disposing of an entity context not free up memory为什么处理实体上下文不会释放内存
【发布时间】:2015-12-08 16:00:43
【问题描述】:

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

为了防止这种情况,我为每 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 次插入处理和创建一个新上下文时,我希望内存使用量保持不变,但前 2000 行似乎是不变的,但之后内存不断攀升,单位是 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 分析器有关。我在我的项目中包含了 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 可修复内存泄漏。还是不知道为什么

【问题讨论】:

  • 你不是一直在添加前50个字段吗?
  • @GregoryHouseMD 你的权利,我的错误,将编辑。无论哪种方式,这只是演示我的问题的简化代码
  • 仅仅因为你正在处理一个对象并不意味着它正在被垃圾收集。您正在运行 Visual Studio 2015 吗?如果没有,请获取社区版并在运行探查器的情况下进行调试。您将看到垃圾收集活动。如果您没有看到任何垃圾收集,那么您可能需要在 ImportBatch 方法结束时进行一些手动垃圾收集。
  • 可能MyService.AddMyEntity 泄漏内存。能把这个方法的代码也贴一下吗?

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


【解决方案1】:

在对象上调用Dispose 不一定会释放内存。当对象不再被任何活动对象引用时,垃圾收集器会从内存中删除对象。调用 Dispose 可能会释放其他资源(例如,在 DbContext 的情况下关闭打开的 SQL 连接),但只有当对象不再被引用时,它才会成为垃圾回收的候选对象。

无法保证垃圾收集器会在特定时间点运行。调用Dispose 当然不会导致它运行。话虽如此,我很惊讶它在内存不足之前没有运行。您可以强制它使用GC.Collect 运行,但这确实没有必要。

您可能仍然有对上下文对象的引用,这导致它们不被视为符合垃圾回收条件。例如,您将 myDbContext 传递给您的服务层中的 AddEntity 方法 - 这是否存储了包含对上下文的反向引用(甚至是间接引用)的内容?

【讨论】:

    【解决方案2】:

    因为每当您调用 ImportBatch(parser) 时,它都会创建一个新的 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); 中,您使用的是来自 MyServiceclass 的 DbContext,而不是您在 using 行中创建的那个。

    【讨论】:

    • 为什么要处理上下文只是为了在它后面创建另一个上下文?这并不比原来的循环好 - 你只是将引用移动到一个类字段。
    • 实际上处理上下文会创建另一个上下文,因为它需要这样。每当 _batchCount > 50 时调用较新的,它会创建另一个并处理旧的。这就是我想在那里尝试做的事情。
    猜你喜欢
    • 2021-12-31
    • 2021-01-11
    • 2013-01-12
    • 2014-12-27
    • 1970-01-01
    • 1970-01-01
    • 2013-10-31
    • 2021-03-04
    相关资源
    最近更新 更多