C#无法清除大型通用集合的内存

时间:2011-03-29 09:18:04

标签: c# generics collections garbage-collection

我将2个非常大的数据集放入内存,执行连接以从第一个集合中过滤掉一个子集,然后尝试销毁第二个集合,因为它使用了大约600MB的系统RAM。问题是下面的代码不起作用。在下面的代码运行后,foreach循环运行大约需要15分钟。在此期间,内存不会从600MB +减少。我做错了吗?

List<APPLES> tmpApples = dataContext.Apples.ToList(); // 100MB
List<ORANGES> tmpOranges = dataContext.Oranges.ToList(); // 600MB

List<APPLES> filteredApples = tmpApples
    .Join(tmpOranges, apples => apples.Id, oranges => oranges.Id, (apples, oranges) => apples).ToList();
tmpOranges.Clear();
tmpOranges = null;
GC.Collect();

注意我稍后重新使用tmpApples,所以我现在不清除它..

6 个答案:

答案 0 :(得分:5)

清除,取消和收集几乎没有任何(积极的)效果。 GC将自动检测何时不再引用对象。此外,只要Join操作运行,tmpApplestmpOranges集合都会被引用,并且所有对象都会被引用。因此无法收集它们。

更好的解决方案是在数据库中进行过滤:

// NOTE That I removed the ToList operations
IQueryable<APPLE> tmpApples = dataContext.Apples;
IQueryable<ORANGE> tmpOranges = dataContext.Oranges;

List<APPLES> filteredApples = tmpApples
    .Join(tmpOranges, apples => apples.Id, 
       oranges => oranges.Id, (apples, oranges) => apples)
    .ToList();

答案 1 :(得分:5)

有几点需要注意:

  • 除非您的dataContext可以清除/垃圾收集,否则很可能会保留对很多对象的引用
  • 调用Clear()然后将变量设置为null是没有意义的,如果你真的没有对列表做任何其他事情。几乎在所有情况下,GC都可以告诉您何时不再使用变量。
  • 据推测,你正在判断这个过程保留了多少内存;我不认为CLR实际上会将内存返回给操作系统,但是已经被垃圾收集释放的内存可用于进一步使用 CLR中。 (编辑:根据下面的评论,CLR可能会释放大对象堆的区域,但我不确定。)

答案 2 :(得分:1)

未收回此数据的原因是因为虽然您正在清除集合(因此集合不再引用项目), DataContext会保留引用,这会导致它保持不变存储器

您必须在完成后立即处置DataContext


更新

好的,你可能已成为large object issue的牺牲品。

答案 3 :(得分:1)

假设这是一个大对象堆问题,您可能会尝试不立即检索所有苹果,而是将它们放入“数据包”中。所以不要打电话

List<APPLE> apples = dataContext.Apples.ToList()

而是尝试将苹果存储在单独的列表中

int packetSize = 100;
List<APPLE> applePacket1 = dataContext.Apples.Take(packetSize);
List<APPLE> applePacket2 = dataContext.Applies.Skip(packetSize).Take(packetSize);

这有帮助吗?

答案 4 :(得分:0)

使用一些分析工具或SOS.dll来查找您的内存所属的位置。如果某些操作需要花费很多时间,这听起来就像是在交换页面文件。

编辑:还要记住,Debug版本将延迟收集不再引用的局部变量,以便于调查。

答案 5 :(得分:-1)

你唯一错误的是显式调用垃圾收集器。你不需要这样做(事实上你不应该这样做)而且史蒂文说你不需要对收藏品做任何事情,他们最终会离开 - 最终。

如果你担心的是15分钟foreach循环的表现,那么你应该发布这个循环。它可能与内存使用无关。