我在使用带有SQL Server Compact Edition的Entity Framework时出现内存泄漏。我的情况:
我有一个大约600MByte的文件。我逐行阅读,创建一个实体类并将其添加到SQL Server CE数据库。内存的增长速度非常快。 Gen 0集合计数器和Gen 2堆大小增长非常快(来自Process Explorer的信息)。如果我理解正确的Gen 2堆是用于大对象。我认为我的实体类是一个大对象。因此,实体框架保存我的对象而不释放它们。我已经尝试分离它们并调用GC.Collect(2)但它没有帮助。
首先我读了这行。然后在解析该行后创建一个对象。然后将其添加到DB。这是我的数据库代码:
DBEntities dbConnection = new DBEntities();
dbConnection.My_Table.AddObject(MyObjectCreatedFromTheLine);
dbConnection.SaveChanges();
// dbConnection.Detach(MyObjectCreatedFromTheLine);
// dbConnection.Dispose();
MyObjectCreatedFromTheLine = null;
dbConnection = null;
我还读到创建的实体类(MyObjectCreatedFromTheLine
)属于DbContext
。所以我为每一行调用这段代码,每次都创建一个新的上下文。
我做错了什么?
答案 0 :(得分:4)
我遇到了这个问题,尝试使用实体框架将50,000多条记录插入到SQL数据库中。实体框架不适用于大量的批量操作(大型插入或删除操作),因此我最终使用了System.Data.SqlClient.SqlBulkCopy库,它更有效,更快捷。我甚至将下面的辅助函数编写为自动映射,因此我不必手动构造SQL Insert语句。 (它的边缘类型独立!我认为)。
基本上工作流程是:IList< MyEntityType> - > 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分钟开始插入到不到一分钟。
答案 1 :(得分:0)
我认为你的方法不对。只需创建一个DBEntities
对象即可保存所有更改。以下内容可能有效;
using(DBEntities dbConnection = new DBEntities())
{
foreach(MyObjectCreatedFromTheLine entity in ListOfMyObjectCreatedFromTheLine)
{
dbConnection.My_Table.AddObject(MyObjectCreatedFromTheLine);
}
dbConnection.SaveChanges();
}
您正在创建一个新的DBEntities
对象foreach实体,这是不对的。只是将dbConnection设置为null并不意味着该对象被丢弃或垃圾收集器不会收集它。实际上,您只是将引用设置为null,该对象仍然在内存中,垃圾收集器将收集该对象。
答案 2 :(得分:0)
我不认为通过数据上下文添加大量实体是最好的方法。对于每个创建的对象,您使用内存,因为数据上下文具有内部第一级缓存,其中对象保留直到上下文被处理。
我不太熟悉EF,也不知道每次持久保存单个对象时是否可以清除缓存。但是,我宁愿选择不使用EF来执行大量插入。
相反,请使用SqlBulkCopy
类。它应该可以解决您的内存问题,并且比使用EF和每个对象插入可以实现的任何速度快一个数量级。
答案 3 :(得分:0)
让你的DBEntities dbConnection =新的DBEntities()离开循环!?
在每次迭代中创建新的对象上下文都是无关紧要的,因为它太荒谬了。
此外,分配需要更多时间,特别是对于像这样的大型对象,更不用说内存开销和释放可能是问题。