繁体   English   中英

内存泄漏实体框架

[英]Memory leak Entity Framework

当我使用实体框架与SQL Server Compact Edition时,我有内存泄漏。 我的情况:

我有一个大约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 所以我为每一行调用此代码,每次创建一个新的上下文。

我究竟做错了什么?

我遇到了这个问题,试图使用实体框架将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分钟到插入到不到一分钟。

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

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

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

我不认为通过数据上下文添加大量实体是最好的方法。 对于每个创建的对象,您使用内存,因为数据上下文具有内部第一级缓存,其中对象保留直到放置上下文。

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

而是使用SqlBulkCopy类。 它应该解决您的内存问题,并且比使用EF和每个对象插入可以实现的任何事情快一个数量级。

从循环中获取DBEntities dbConnection = new DBEntities()!

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM