2012-07-12 58 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()退出循環!

在每次迭代中創建新的對象上下文是無關緊要的,因爲它是荒謬的。

也需要更多時間進行分配,特別是對於像這樣的大對象,更不用說內存開銷和可能是問題的釋放。