2012-07-12 48 views
3

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

我有一个约600MByte大的文件。我逐行阅读它,创建一个实体类并将其添加到SQL Server CE数据库中。记忆力正在迅速增长。 Gen 0 Collections计数器和Gen 2堆大小增长非常快(来自Process Explorer的信息)。如果我理解正确的第二代堆是为了大件物品。我认为我的实体课是一个大对象。所以实体框架保存我的对象,不释放它们。我已经尝试分离它们并调用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。所以我为每一行调用这个代码,每次创建一个新的上下文。

我在做什么错?

+2

您应该只使用一个上下文,将所有对象添加到上下文中,然后调用'SaveChanges'一次。还可以使用'using'语法糖强制在你的上下文实例中调用Dispose'。 – ken2k 2012-07-12 11:35:58

+3

你为什么不处理你的dbConnection? – KingCronus 2012-07-12 11:36:38

+0

我使用了dbConnection.Dispose(),但它没有帮助。同样在开始时,我只用了一个上下文,问题也是一样的。然后我读到,然后问题是这个情节保持指向这些对象的指针。所以我尝试这种方式,每次创建一个新的上下文。 – 2012-07-12 12:13:10

回答

0

我认为你的做法是不正确的。只需创建一个DBEntities对象即可保存所有更改。类似以下内容可能会起作用;

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

您正在创建一个新的DBEntities对象的foreach实体,这简直是不正确的。只要将dbConnection设置为null并不意味着该对象被丢弃或垃圾收集器不会收集它。实际上,你只是将引用设置为null,对象仍然在内存中,垃圾收集器将收集该对象。

+0

我尝试一下。现在我的内存泄漏正在增长得更快。我确信直到我没有在堆中调用SaveChanges()。所以工作组正在增长。 – 2012-07-12 12:36:05

+0

你监测记忆多久了?为了被称为​​内存泄漏,在相对较短的时间内消耗的内存不应该减少(或增加)。 – daryal 2012-07-12 12:38:33

0

我不认为通过数据上下文添加大量实体是最好的方法。对于每个创建的对象,由于数据上下文具有内部第一级高速缓存,因此会消耗内存,其中的对象将一直保留到处理上下文为止

我不太了解EF,也不知道每次你坚持单个对象时,缓存是否可以被清除。但是,我宁愿选择不使用EF来执行大量插入操作。可以使用SqlBulkCopy类。它应该能够解决你的记忆问题,并且它的速度比EF和每个对象插入所能达到的速度快一个数量级。

4

我遇到了这个问题,试图使用实体框架将50,000多条记录插入SQL数据库。实体框架不适用于庞大的批量操作(大插入或删除操作),所以我最终使用了System.Data.SqlClient.SqlBulkCopy库,这个库更加高效快捷。我甚至写了下面的帮助函数来自动映射,所以我不必手动构造SQL插入语句。 (我认为它的边际类型是独立的!)。

基本上工作流程是:IList的<MyEntityType> - >数据表 - > 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分钟过去了一分钟之内插入到。

0

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

在每次迭代中创建新的对象上下文是无关紧要的,因为它是荒谬的。

也需要更多时间进行分配,特别是对于像这样的大对象,更不用说内存开销和可能是问题的释放。