强制数组的垃圾收集,C#

我有一个问题,其中一对三维数组分配了大量的内存,程序有时需要用更大/更小的内容替换它们并抛出OutOfMemoryException。

示例:有5个分配的96MBarrays(200x200x200,每个条目中有12个字节的数据),程序需要用210x210x210(111MB)替换它们。 它以类似于这样的方式实现:

array1 = new Vector3[210,210,210]; 

其中array1-array5与先前使用的字段相同。 这应该将旧数组设置为垃圾收集的候选者,但看起来GC不能足够快地运行并在分配新数组之前分配旧数组 – 这会导致OOM – 而如果它们在新分配之前释放,则空间应该是足够。

我正在寻找的是一种做这样的事情的方法:

 GC.Collect(array1) // this would set the reference to null and free the memory array1 = new Vector3[210,210,210]; 

我不确定完整的垃圾收集是否是一个好主意,因为该代码可能(在某些情况下)需要经常执行。

有没有正确的方法呢?

这不是原始问题“如何强制GC”的确切答案,但我认为它将帮助您重新检查您的问题。

看到你的评论后,

  • 放GC.Collect(); 确实似乎有所帮助,尽管它还没有完全解决问题 – 由于某种原因,当分配大约1.3GB时程序仍然崩溃(我正在使用System.GC.GetTotalMemory(false);来查找分配的实际数量)。

我怀疑你可能有内存碎片 。 如果对象很大(如果我没记错的话,在.net 2.0 CLR下85000字节,我不知道它是否已被更改),该对象将被分配在一个特殊的堆, 大对象堆(LOH)中 。 GC确实回收了LOH中无法访问的对象所使用的内存,但是,由于性能的原因, 在LOH 中不执行压缩,因为它对其他堆(gen0,gen1和gen2) 执行压缩

如果你经常分配和释放大对象,它会使LOH碎片化,即使你有更多的可用内存而不是你需要的内存,你可能不再有连续的内存空间,因此,会得到OutOfMemoryexception。

我现在可以想到两个解决方法。

  1. 转移到64位机器/操作系统并利用它:)(最简单,但可能最难,取决于您的资源限制)
  2. 如果你不能做#1,那么首先尝试分配一个巨大的内存并使用它们(可能需要编写一些辅助类来操作一个较小的数组,实际上它存在于一个更大的数组中)以避免碎片。 这可能有点帮助,但它可能无法完全解决问题,您可能不得不处理复杂性。

好像你遇到了LOH(大对象堆)碎片问题。

大对象堆

CLR内部大对象堆未被发现

您可以使用SOS检查是否存在loh碎片问题

查看此问题以获取如何使用SOS检查loh的示例。

强制垃圾收集并不总是一个好主意(在某些情况下它实际上可以促进对象的生命周期)。 如果必须,你会使用:

 array1 = null; GC.Collect(); array1 = new Vector3[210,210,210]; 

这不仅仅是大对象堆碎片吗? 在大对象堆上分配> 85,000个字节的对象。 GC释放了此堆中的空间,但从未压缩剩余的对象。 这可能导致无法连续的内存以成功分配大对象。

艾伦。

如果我不得不推测你的问题并不是你从Vector3 [200,200,200]到Vector3 [210,210,210],但很可能你在此之前有类似的先前步骤:

 即   
     //首先你有
    的Vector3 [10,10,10];
     // 然后
    的Vector3 [20,20,20];
     //那么也许吧
    的Vector3 [30,30,30];
     // .. 等等 ..
     // ...
     // 然后
    的Vector3 [200200200];
     //最后你试试
     Vector3 [210,210,210] //你得到一个OutOfMemoryException ..

如果这是真的,我会建议更好的分配策略。 尝试过度分配 – 每次可能加倍大小而不是总是只分配你需要的空间。 特别是如果需要固定缓冲区的对象使用这些数组(即,如果它与本机代码有关系)

所以,而不是上面,有这样的事情:

  // first start with an arbitrary size Vector3[64,64,64]; // then double that Vector3[128,128,128]; // and then.. so in thee steps you go to where otherwise // it would have taken you 20.. Vector3[256,256,256]; 

他们可能没有被收集,因为他们被引用到你没想到的地方。

作为测试,请尝试更改对WeakReferences的引用,并查看是否可以解决您的OOM问题。 如果没有,那么你在其他地方引用它们。

我理解你正在尝试做什么,推动立即垃圾收集可能不是正确的方法(因为GC的方式很微妙,很快就会生气)。

也就是说,如果你想要这个function,为什么不创建它呢?

 public static void Collect(ref object o) { o = null; GC.Collect(); } 

OutOfMemoryexception在内部自动触发GC循环一次,并在实际将exception抛出到代码之前再次尝试分配。 你可以拥有OutOfMemoryexception的唯一方法是,如果你持有过多内存的引用。 通过将引用赋值为null,尽快清除引用。

部分问题可能是您正在分配一个多维数组,该数组在大对象堆上表示为单个连续的内存块( 此处有更多详细信息)。 这可以阻止其他分配,因为没有可用的空闲连续块,即使某处仍有一些空闲空间,因此也就是OOM。

尝试将其分配为锯齿状数组 – Vector3 [210] [210] [210] – 将数组分布在内存而不是单个块中,并查看是否可以改善问题

约翰,创建对象> 85000字节将使对象最终在大对象堆中。 从不压缩大对象堆,而是再次重用可用空间。 这意味着如果每次都分配更大的数组,最终可能会出现LOH碎片化的情况,因此也就是OOM。

您可以通过在OOM点断调试器并获得转储来validation这种情况,通过连接错误( http://connect.microsoft.com )将此转储提交给MS将是一个很好的开始。

我可以向你保证的是,GC会做正确的事情,试图满足你的分配请求,这包括启动GC来清理旧垃圾以满足新的分配请求。

我不知道在Stackoverflow上共享内存转储的策略是什么,但我很乐意看看更多地了解你的问题。