C# 实体数据查询与内存泄漏
我在一个循环中下载了很多数据,但在一些操作之后我删除了它们,但我看到的是内存分配增长非常快,只有几秒钟和1GB,所以如何在每次迭代后进行清理C# 实体数据查询与内存泄漏,c#,entity-framework,memory-leaks,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 da
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();
}
}
这里的问题是否可能与DataContext有关。它们中的许多在执行查询时会缓存信息或存储附加信息,因此其内存占用会随着时间的推移而增加。我会先用探查器检查一下,但如果这是您的问题,您可能需要在每次X请求后重新创建一个新的datacontext(使用不同的X值进行实验,看看什么效果最好)
我还想指出的是,大多数人都有很强的记忆力。在开始进行这些类型的优化之前,您应该确保使用的内存超过了真正可以接受的内存。GC还将开始更积极地清除内存,因为您的可用内存更少。它不会过早优化(您也不应该这样做)。您可以做的第一件事是禁用更改跟踪,因为您没有更改代码中的任何数据。这样可以防止加载的对象附着到上下文: 对于
DbContext
(EF>=4.1):
以及:
编辑
对于EF 4.0,您可以保留查询,但使用块在中添加以下内容作为前两行:
contex.AIMRInputs.MergeOption = MergeOption.NoTracking;
contex.Values.MergeOption = MergeOption.NoTracking;
这将禁用ObjectContext
的更改跟踪
编辑2
…特别是以下@James Reategui的评论AsNoTracking
减少了内存占用:
这通常是正确的(就像在这个问题的模型/查询中),但并不总是如此!实际上使用AsNoTracking
在内存使用方面可能会适得其反
当对象在内存中具体化时,AsNoTracking
做什么
- 第一:它不会将实体附加到上下文,因此不会在上下文的状态管理器中创建条目。这些条目消耗内存。使用POCOs时,条目包含实体首次加载/附加到上下文时的属性值快照-基本上是对象本身之外的所有(标量)属性的副本。因此,当未应用
AsNoTracking
时,消耗的内存(大约)是对象大小的两倍
- 第二:另一方面,当实体没有连接到上下文时,EF无法利用键值和对象引用标识之间的标识映射的优势。这意味着具有相同键的对象将被物化多次,这将消耗额外的内存,而不使用
AsNoTracking
EF将确保每个键值只物化一次实体
第二点在加载相关实体时变得尤为重要。简单的例子:
比如说,我们有一个订单
和一个客户
实体,一个订单有一个客户订单。客户
。假设Order
对象的大小为10字节,而Customer
对象的大小为20字节。现在我们运行此查询:
var orderList = context.Orders
.Include(o => o.Customer).Take(3).ToList();
假设所有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();
具体化数据包括:
- 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
,也不要惊讶。最后,您必须测量内存占用量,以便做出支持或反对的可靠决策,因为notracking
是ToLi
var orderList = context.Orders
.Include(o => o.Customer).Take(3).ToList();
var orderList = context.Orders.AsNoTracking()
.Include(o => o.Customer).Take(3).ToList();