ObjectContext正在为分离的实体泄漏内存

时间:2011-10-02 21:26:02

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

我已经使用内存分析器检查了这一点,并且没有真正的实体保留在内存中散列集,字典和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)");
    }
}

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

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

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

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

这应该会缩短上下文的生命周期。

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