【问题标题】:Memory leak Entity Framework内存泄漏实体框架
【发布时间】:2012-07-12 11:32:33
【问题描述】:

在将实体框架与 SQL Server Compact Edition 一起使用时出现内存泄漏。我的情况:

我有一个大约 600MByte 大的文件。我逐行阅读,创建一个实体类并将其添加到 SQL Server CE 数据库中。内存因此增长得非常快。 Gen 0 Collections 计数器和 Gen 2 Heap Size 增长非常快(来自 Process Explorer 的信息)。如果我理解正确的 Gen 2 Heap 是用于大对象的。我认为我的实体类是一个大对象。所以 Entity Framework 保存我的对象而不释放它们。我已经尝试将它们分离并调用 GC.Collect(2) 但它没有帮助。

首先我读了这行。然后在解析行后创建一个对象。然后将其添加到数据库。这是我的数据库代码:

DBEntities dbConnection = new DBEntities();
dbConnection.My_Table.AddObject(MyObjectCreatedFromTheLine);
dbConnection.SaveChanges();
//  dbConnection.Detach(MyObjectCreatedFromTheLine);
//  dbConnection.Dispose();
MyObjectCreatedFromTheLine = null;
dbConnection = null;

我还读到创建的实体类(MyObjectCreatedFromTheLine)属于DbContext。所以我为每一行调用这段代码,每次都创建一个新的上下文。

我做错了什么?

【问题讨论】:

  • 您应该只使用一个上下文,将所有对象添加到上下文然后调用SaveChanges 一次。还可以使用using 语法糖来强制对您的上下文实例进行Dispose 调用。
  • 你为什么不处理你的 dbConnection?
  • 我使用了 dbConnection.Dispose() 但它没有帮助。同样一开始我只使用了一个上下文,问题是一样的。然后我读到,问题是上下文保留了指向对象的指针。所以我尝试这种方式,每次都创建一个新的上下文。
  • 使用我的 SqlCeBulkCopy 库快速插入到 SQL Server Compact:sqlcebulkcopy.codeplex.com
  • 所以 EntityFramework 不适合批量插入。但是您可以关闭 AutoDetectChangesEnabled 以减少相当多的内存使用量。在调用GC.Collect 时,还要定期更新您的 DbContext。它不会解决问题,但会让问题变得可以忍受。

标签: c# .net entity-framework entity-framework-4 sql-server-ce


【解决方案1】:

我在尝试使用实体框架将 50,000 多条记录插入 SQL 数据库时遇到了这个问题。实体框架不适用于大型批量操作(大型插入或删除操作),因此我最终使用了 System.Data.SqlClient.SqlBulkCopy 库,它更高效、更快。我什至编写了以下帮助函数来自动映射,因此我不必手动构造 SQL 插入语句。 (它与类型无关!我认为)。

基本上工作流程是:IList -> DataTable -> SqlBulkCopy

public static void BulkInsert<T>(string connection, string tableName, IList<T> list)
    {
        using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepNulls))
        {
            bulkCopy.BatchSize = list.Count;
            bulkCopy.DestinationTableName = tableName;
            bulkCopy.BulkCopyTimeout = 3000;

            var table = new DataTable();
            var props = TypeDescriptor.GetProperties(typeof(T))
                //Dirty hack to make sure we only have system data types 
                //i.e. filter out the relationships/collections
                                       .Cast<PropertyDescriptor>()
                                       .Where(propertyInfo => propertyInfo.PropertyType.Namespace.Equals("System"))
                                       .ToArray();

            foreach (var propertyInfo in props)
            {
                bulkCopy.ColumnMappings.Add(propertyInfo.Name, propertyInfo.Name);
                table.Columns.Add(propertyInfo.Name, Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType);
            }

            var values = new object[props.Length];
            foreach (var item in list)
            {
                for (var i = 0; i < values.Length; i++)
                {
                    values[i] = props[i].GetValue(item);
                }

                table.Rows.Add(values);
            }

            bulkCopy.WriteToServer(table);
        }
    }

在我的示例中,我从 15-20 分钟插入到不到一分钟。

【讨论】:

    【解决方案2】:

    我认为你的方法不对。只需创建一个DBEntities 对象即可保存所有更改。像下面这样的东西可能会起作用;

    using(DBEntities dbConnection = new DBEntities())
    {
        foreach(MyObjectCreatedFromTheLine entity in ListOfMyObjectCreatedFromTheLine)
        {
            dbConnection.My_Table.AddObject(MyObjectCreatedFromTheLine);
        }
        dbConnection.SaveChanges();
    }
    

    您正在为每个实体创建一个新的DBEntities 对象,这根本不对。只是将 dbConnection 设置为 null 并不意味着对象已被释放或垃圾收集器不会收集它。其实你只是将引用设置为null,对象还在内存中,垃圾回收器会回收对象。

    【讨论】:

    • 我试试看。现在我的内存泄漏增长得更快。我敢肯定,直到我不只是在堆中调用 SaveChanges() 。所以工作集正在增长。
    • 你监控内存多久了?为了被称为​​内存泄漏,消耗的内存在较短的时间内不得减少(或增加)。
    【解决方案3】:

    我不认为通过数据上下文添加大量实体是最好的方法。对于每个创建的对象,您都会消耗内存,因为数据上下文有一个内部的一级缓存,对象会一直保留到上下文被释放。

    我不太了解EF,也不知道每次持久化一个对象时是否可以清除缓存。但是,我宁愿选择根本不使用 EF 来执行大量插入。

    改为使用SqlBulkCopy 类。它应该可以解决您的内存问题,而且它比您使用 EF 和每个对象插入所能实现的任何东西都要快一个数量级。

    【讨论】:

      【解决方案4】:

      让您的 DBEntities dbConnection = new DBEntities() 退出循环!?

      在每次迭代中创建新的对象上下文既不相关又荒谬。

      而且分配需要更多时间,尤其是对于像这样的大对象,更不用说内存开销和释放,这可能是问题所在。

      【讨论】:

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