实体数据查询和内存泄漏

时间:2012-03-23 17:04:31

标签: c# entity-framework memory-leaks

我在循环中下载了大量数据但是在一些操作之后我删除它们但我看到的是内存分配正在快速增长,几秒钟和1GB,所以如何在每次迭代后清理?

    using (var contex = new DB)
    {

        var inputs = contex.AIMRInputs.Where(x => x.Input_Type == 1);

        foreach (var input in inputs)
        {
            var data = contex.Values.Where(x => x.InputID == input.InputID).OrderBy(x => x.TimeStamp).ToList();

            if (data.Count == 0) continue;
            foreach (var value in data)
            {
               Console.WriteLine(Value.property);
            }
            data.Clear();


        }
    }

2 个答案:

答案 0 :(得分:12)

您可以做的第一件事是禁用更改跟踪,因为您没有更改代码中的任何数据。这可以防止加载的对象附加到上下文:

对于DbContext(EF> = 4.1):

var inputs = contex.AIMRInputs.AsNoTracking()
    .Where(x => x.Input_Type == 1);

var data = contex.Values.AsNoTracking()
    .Where(x => x.InputID == input.InputID)
    .OrderBy(x => x.TimeStamp)
    .ToList();

修改

对于EF 4.0,您可以按原样保留查询,但将以下内容添加为using块中的前两行:

contex.AIMRInputs.MergeOption = MergeOption.NoTracking;
contex.Values.MergeOption = MergeOption.NoTracking;

这会禁用ObjectContext的更改跟踪。

修改2

...特别是参考@James Reategui的评论,AsNoTracking减少了内存占用:

这通常是正确的(就像在这个问题的模型/查询中)但并非总是如此!实际上使用AsNoTracking会对内存使用产生反作用。

当对象在内存中实现时,AsNoTracking会做什么?

  • 首先:它不会将实体附加到上下文,因此不会在上下文的状态管理器中创建条目。这些条目消耗内存。使用POCO时,条目包含实体首次加载/附加到上下文时的属性值的快照 - 基本上是除了对象本身之外的所有(标量)属性的副本。因此,当未应用AsNoTracking时,comsumed内存大约是对象大小的两倍。

  • 第二:另一方面,当实体没有附加到上下文时,EF无法利用键值和对象引用标识之间的标识映射的优势。这意味着具有相同键的对象将被多次实现,这将增加内存,而不使用AsNoTracking EF将确保实体仅在每个键值实现一次。

当加载相关实体时,第二点变得尤为重要。简单的例子:

说,我们有OrderCustomer实体,订单有一个客户Order.Customer。假设Order对象的大小为10字节,Customer对象的大小为20字节。现在我们运行此查询:

var orderList = context.Orders
    .Include(o => o.Customer).Take(3).ToList();

假设所有3个已加载的订单都分配了相同的客户。因为我们没有禁用跟踪EF将实现:

  • 3个订单对象= 3x10 = 30个字节
  • 1个客户对象= 1x20 = 20字节(因为上下文认识到客户对于所有3个订单都是相同的,它只实现了一个客户对象)
  • 原始值= 3x10 = 30字节的3个订单快照条目
  • 1个客户快照条目,原始值= 1x20 = 20字节

总和:100字节

(为简单起见,我假设具有复制属性值的上下文条目与实体本身具有相同的大小。)

现在我们使用禁用的更改跟踪运行查询:

var orderList = context.Orders.AsNoTracking()
    .Include(o => o.Customer).Take(3).ToList();

具体化数据为:

  • 3个订单对象= 3x10 = 30个字节
  • 3(!)客户对象= 3x20 = 60字节(无标识映射=每个键有多个对象,所有三个客户对象都具有相同的属性值,但它们仍然是内存中的三个对象)
  • 没有快照条目

总和:90字节

因此,在这种情况下,使用AsNoTracking查询会减少10个字节的内存。

现在,使用5个订单(Take(5))进行相同的计算,同样所有订单都拥有相同的客户:

没有AsNoTracking

  • 5个订单对象= 5x10 = 50个字节
  • 1个客户对象= 1x20 = 20字节
  • 5个订单快照条目,原始值= 5x10 = 50字节
  • 1个客户快照条目,原始值= 1x20 = 20字节

总和:140字节

使用AsNoTracking

  • 5个订单对象= 5x10 = 50个字节
  • 5(!)customer objects = 5x20 = 100 byte
  • 没有快照条目

总和:150字节

这次使用AsNoTracking要贵10个字节。

上面的数字非常粗糙,但某处是一个收支平衡点,使用AsNoTracking可能需要更多内存。

使用AsNoTracking与否之间的内存消耗差异很大程度上取决于查询,模型中的关系以及查询加载的具体数据。例如:当上述示例中的订单(或大部分)具有不同的客户时,AsNoTracking在内存消耗方面总是更好。

结论:AsNoTracking主要用作提高查询性能的工具,而非内存使用。在许多情况下,它也会消耗更少的内存。但如果特定查询需要更多内存AsNoTracking,请不要感到惊讶。最后,您必须衡量内存占用量,以便做出有利于或反对AsNoTracking的明确决策。

答案 1 :(得分:1)

如果此处的问题可能与DataContext有关。其中许多缓存信息或在执行查询时存储其他信息,因此内存占用将随着时间的推移而增长。我会首先检查一个分析器,但如果这是你的问题,你可能需要在每次X请求后重新创建一个新的datacontext(尝试使用不同的X值来看看哪种方法效果最好)。

我还想指出,大多数人往往有很多记忆。在开始进行这些类型的优化之前,您应该确定使用的内存比真正可接受的内存多。由于可用内存较少,GC还将开始更积极地清除内存。它不会过早地优化(也不应该)。