我遇到一个问题,其中一对三维数组分配了大量内存,程序有时需要用更大/更小的内存替换它们并抛出OutOfMemoryException。
示例:有5个分配的96MB阵列(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];
我不确定完整的垃圾收集是否是一个好主意,因为该代码可能(在某些情况下)需要经常执行。
有没有正确的方法呢?
答案 0 :(得分:15)
这不是原始问题“如何强制使用GC”的确切答案,但我认为它可以帮助您重新检查您的问题。
看到你的评论后,
我怀疑你可能有内存碎片。如果对象很大(如果我没记错的话,在.net 2.0 CLR下85000字节,我不知道它是否已被更改),该对象将被分配在一个特殊的堆中,大对象堆(LOH) 即可。 GC确实回收了LOH中无法访问的对象所使用的内存,但由于性能的原因,在LOH中不执行压缩,,因为它对其他堆(gen0,gen1和gen2)执行压缩。
如果你经常分配和释放大对象,它会使LOH碎片化,即使你有更多的可用内存而不是你需要的内存,你可能不再有连续的内存空间,因此,会得到OutOfMemory异常。
我现在可以想到两个解决方法。
答案 1 :(得分:9)
似乎你遇到了LOH(大对象堆)碎片问题。
CLR Inside Out Large Object Heap Uncovered
您可以使用SOS检查是否存在loh碎片问题
检查此question以获取如何使用SOS检查loh的示例。
答案 2 :(得分:7)
强制垃圾收集并不总是一个好主意(在某些情况下它实际上可以促进对象的生命周期)。如果必须,您可以使用:
array1 = null;
GC.Collect();
array1 = new Vector3[210,210,210];
答案 3 :(得分:4)
这不仅仅是大对象堆碎片吗?对象>在大对象堆上分配85,000个字节。 GC释放了此堆中的空间,但从未压缩剩余的对象。这可能导致无法连续的内存成功分配大对象。
艾伦。
答案 4 :(得分:2)
如果我不得不推测你的问题并不是你从Vector3 [200,200,200]到Vector3 [210,210,210],但很可能你之前有类似的前一步:
i.e. // first you have Vector3[10,10,10]; // then Vector3[20,20,20]; // then maybe Vector3[30,30,30]; // .. and so on .. // ... // then Vector3[200,200,200]; // and eventually you try Vector3[210,210,210] // and you get an 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];
答案 5 :(得分:1)
他们可能没有被收集,因为他们被引用到你没想到的地方。
作为测试,请尝试更改对WeakReferences的引用,看看是否可以解决您的OOM问题。如果没有,那么你就会在其他地方引用它们。
答案 6 :(得分:1)
我理解你正在尝试做什么,推动立即进行垃圾收集可能不是正确的方法(因为GC的方式很微妙而且很快就会生气)。
那就是说,如果你想要那个功能,为什么不创建呢?
public static void Collect(ref object o)
{
o = null;
GC.Collect();
}
答案 7 :(得分:0)
OutOfMemory异常在内部自动触发GC循环一次,并在实际将异常抛出到代码之前再次尝试分配。你可以拥有OutOfMemory异常的唯一方法就是你持有过多内存的引用。尽快清除引用,将它们指定为null。
答案 8 :(得分:0)
部分问题可能是您正在分配一个多维数组,该数组在大对象堆上表示为单个连续的内存块(更多详细信息here)。这可以阻止其他分配,因为没有一个空闲的连续块可供使用,即使某处仍有一些空闲空间,因此也就是OOM。
尝试将其分配为锯齿状数组 - Vector3 [210] [210] [210] - 将数组分布在内存而不是单个块中,并查看是否有所改善
答案 9 :(得分:0)
John,创建对象> 85000字节将使对象最终在大对象堆中。从不压缩大对象堆,而是再次重用可用空间。 这意味着如果每次都分配更大的数组,最终可能会出现LOH碎片化的情况,因此也就是OOM。
你可以通过在OOM点断调试器并获得转储来验证这种情况,通过连接错误(http://connect.microsoft.com)将此转储提交给MS将是一个很好的开始。
我可以向你保证,GC会做正确的事情,试图满足你的分配请求,这包括启动GC清理旧垃圾以满足新的分配请求。
我不知道在Stackoverflow上共享内存转储的策略是什么,但我很乐意更多地了解您的问题。