实体数据查询和内存泄漏

kos*_*kov 4 c# memory-leaks entity-framework

我在循环中下载了大量数据但是在一些操作之后我删除了它们但是我看到内存分配正在快速增长,几秒钟和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();


        }
    }
Run Code Online (Sandbox Code Playgroud)

Sla*_*uma 12

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

对于DbContext(EF> = 4.1):

var inputs = contex.AIMRInputs.AsNoTracking()
    .Where(x => x.Input_Type == 1);
Run Code Online (Sandbox Code Playgroud)

和:

var data = contex.Values.AsNoTracking()
    .Where(x => x.InputID == input.InputID)
    .OrderBy(x => x.TimeStamp)
    .ToList();
Run Code Online (Sandbox Code Playgroud)

编辑

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

contex.AIMRInputs.MergeOption = MergeOption.NoTracking;
contex.Values.MergeOption = MergeOption.NoTracking;
Run Code Online (Sandbox Code Playgroud)

这会禁用更改跟踪ObjectContext.

编辑2

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

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

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

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

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

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

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

var orderList = context.Orders
    .Include(o => o.Customer).Take(3).ToList();
Run Code Online (Sandbox Code Playgroud)

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

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

总和:100字节

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

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

var orderList = context.Orders.AsNoTracking()
    .Include(o => o.Customer).Take(3).ToList();
Run Code Online (Sandbox Code Playgroud)

物化数据是:

  • 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(!)客户对象= 5x20 = 100字节
  • 没有快照条目

总和:150字节

这次使用的AsNoTracking是10字节更贵.

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

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

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