【问题标题】:ObjectContext is leaking memory for detached entitiesObjectContext 正在泄漏分离实体的内存
【发布时间】:2011-11-29 13:40:21
【问题描述】:

我已经使用内存分析器检查了这一点,并且没有真正的实体留在内存中但是散列集、字典和 EntityKey 对象——但我发现无法断开这些引用。

这么简单的问题:如何阻止上下文(或其 ObjectStateManager)的大小无限增长

[是的,我知道应该避免长期存在的上下文,但在这种情况下,这是一个复杂的分析运行,需要加载多个分层数据(下面的示例只是一个最小的问题演示)所以最后这是一个“短暂”的单操作上下文。]

复制步骤:

  • 创建一个新的控制台应用程序
  • 为 Northwind 数据库创建 EF 模型(使用一些真正的 SQL Server 或从 Compact Samples 文件夹复制 Northwind.sdf)
  • 使用以下代码:

代码[已更新,不再需要真正的数据库连接]:

class Program
{
    static void Main()
    {
        const double MiB = 1024 * 1024;
        using ( var context = new NorthwindEntities() )
        {
            var last = GC.GetTotalMemory(true) / MiB;
            Console.WriteLine("before run: {0:n3} MiB", last);
            var id = 0;
            while ( true )
            {
                Run(context, ref id);

                GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
                GC.WaitForPendingFinalizers();
                var current = GC.GetTotalMemory(true) / MiB;
                Console.WriteLine("after run: {0:n3} MiB (+{1:n3} MiB)", current, current - last);
                last = current;

                if ( Console.KeyAvailable )
                    break;
                Console.WriteLine(new string('-', 100));
            }
        }
    }

    static void Run(NorthwindEntities context, ref int id)
    {
        for ( int i = 0; i < 100000; i++ )
        {
            var category = new Category { Category_ID = ++id };
            category.EntityKey = new EntityKey("NorthwindEntities.Categories", "Category_ID", id);
            var product = new Product { Product_ID = id, Category_ID = id };
            product.EntityKey = new EntityKey("NorthwindEntities.Products", "Product_ID", id);
            product.Category = category;
            context.Attach(product);
            context.Detach(product);
            context.Detach(category);
        }

        var ctr = 0;
        Console.WriteLine("Enumerating living/attached objects:");
        const EntityState AllStates = EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged;
        foreach ( var entry in context.ObjectStateManager.GetObjectStateEntries(AllStates) )
            Console.WriteLine("  #{0} [{1}] {2}", ++ctr, entry.EntityKey, entry.Entity);
        if ( ctr == 0 )
            Console.WriteLine("  NOTHING (as expected)");
    }
}

【问题讨论】:

  • 好的,你的结果是什么?您是否使用了内存分析器?
  • 运行多少次以及多少分钟才​​能达到 0.1 / 0.5 / 1.0 / 1.5 GB?
  • @Henk Holterman:是的,我使用了内存分析器,只看第一句话。我的示例使用 SQL Server Compact 的性能非常差,但真正的应用程序使用的是 SQLite,它可以在 10% 的时间内在每个 SaveChanges() 上保存 10,000 个新实体,因此很快就浪费了一个完整的 1 GiB不求回报。此处的示例每次运行损失约 0.5MiB。
  • Fwiw,我确实确认了您之前版本的结果。我认为您应该重构和重新设计以使用寿命较短的上下文。
  • 我主要在 ASP.NET 和 WCF 项目中使用分离的对象。该功能用于将对象传输到不同的上下文,而不是手动管理内存。上下文是短暂的,不要与之抗争。

标签: c# .net entity-framework memory-leaks


【解决方案1】:

我的理解是,分离是为了从实体中删除上下文,而不是从上下文中删除实体。不要相信上下文来删除对实体(或其内部)的引用。

我同意这种泄漏是一个问题,但是很多人(包括我自己)都尝试过并且未能阻止 EF 上下文无限增长(只要查询正在运行)。

作为一种建议的解决方案,也许不是将数据库作为计算的“工作空间”,而是可以在您自己的内存表示中重新创建数据库结构,进行处理,然后转换回D b。如果您有大量数据,您可以使用临时文件。

这应该会缩短上下文的寿命。

或者,也许考虑使用 EF 以外的其他东西(至少对于处理密集型部分),因为它可能不适合您的情况。也许较低级别的东西,比如 DataReader 会更适合您的情况。

【讨论】:

  • 我将不得不复制几乎所有 EF 的核心内容:在加载时自动连接所有 1:1 和 1:n 关系并跟踪应写回存储的每个更改。
【解决方案2】:

由于我只是在调用 SaveChanges() 后直接分离实体,我现在计算分离实体的数量,当计数器达到 10,000 时,我从上下文中分离所有仍然存在(和需要)的对象并创建一个我将所有分离的对象附加到的新上下文。 缺点:EntityReferences 和 EntityCollections 的 IsLoaded 属性现在始终为 false(但我不依赖于此)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-20
    • 2013-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多