强制对数组进行垃圾回收,C#
-
12-09-2019 - |
题
我遇到一个问题,几个 3 维数组分配大量内存,程序有时需要用更大/更小的数组替换它们并抛出 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];
我不确定完整的垃圾收集是否是一个好主意,因为该代码可能(在某些情况下)需要相当频繁地执行。
有没有正确的方法来做到这一点?
解决方案
这不是一个确切的答复到原来的问题,"如何力GC',然而,我认为它会帮助你重新审视你的问题。
在看到您的意见,
- 把GC。收集();似乎有帮助,altought它仍然没有彻底解决问题-因为某些原因的程序仍然崩溃时大约1.3GB分配(我采用的系统。GC。GetTotalMemory(false);来找到真正的分配数量).
我会怀疑你可能已经 记忆的碎片.如果对象是大(85000字节。网2.0CLR如果我没有记错的话,我不知道是否已经改变或不是),对象将被分配在一个特殊堆, 大堆对象(卢).GC没回收的存储器被使用的可达目的在陆,但是, 它不执行实, 在陆,因为它会给其他堆(gen0,1和2),由于业绩。
如果你这样做常常分配和释放的大型物体,它将使罗的碎片,即使你有更多的免费存储在总比你需要什么,你不可以有一个连续的存储空间了,因此,将获得内存例外。
我可以想两种解决方法在这个时刻。
- 移动到64位的机器/操作系统,并利用它:)(最简单的,但可能难以及取决于资源的限制)
- 如果你无法做到#1,然后再分配一个巨大的吸盘存储器的第一并使用他们的(可能需要编写一些辅助类操纵的一个小型阵列,这实际上属于一种大阵列),以避免碎片。这可以帮助一点,然而,它不可能完全解决的问题和可能必须处理的复杂性。
其他提示
强制垃圾收集并不总是一个好主意(它实际上可以促进对象在某些情况下的寿命)。如果你有,你可以使用:
array1 = null;
GC.Collect();
array1 = new Vector3[210,210,210];
这不只是大型对象堆碎片?对象> 85000个字节被分配在大对象堆。该GC释放空间在这个堆,但从来没有压实其余的对象。这可能会导致insufficent连续存储器成功地分配一个大对象。
艾伦。
如果我不得不猜测你的问题是不是真的,你是从的Vector3 [200200200]去一个的Vector3 [210210210]不过,最有可能你有这样一个类似前前面的步骤:
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];
他们可能不会得到收集,因为他们被引用的地方你不期待。
作为测试,请尝试更改引用在WeakReferences 代替,看看是否能解决您的OOM问题。如果没有的话你引用它们在其他地方。
我明白你正在试图做的,推动直接垃圾收集了可能是不正确的方法(因为GC处于微妙的方式和快愤怒)。
这是说,如果你的要的那个功能,为什么不创建它?
public static void Collect(ref object o)
{
o = null;
GC.Collect();
}
这是OutOfMemory例外内部触发GC自动循环一次,居然抛出异常到你的代码之前再次尝试进行分配。你可以有OutOfMemory异常的唯一方法是,如果你持有引用太多内存。通过赋予他们空,只要你可以清除的引用。
部分问题可能是您正在分配一个多维数组,该数组表示为大对象堆上的单个连续内存块(更多详细信息 这里)。这可能会阻止其他分配,因为没有可用的连续块,即使某处仍有一些可用空间,因此出现 OOM。
尝试将其分配为锯齿状数组 - Vector3[210][210][210] - 将数组分布在内存周围而不是作为单个块,看看这是否会改善问题
John,创建 > 85000 字节的对象将使对象最终位于大对象堆中。大对象堆永远不会被压缩,而是再次重用可用空间。这意味着,如果每次都分配更大的数组,最终可能会出现 LOH 碎片化的情况,从而导致 OOM。
您可以通过在 OOM 时中断调试器并获取转储来验证情况是否如此,然后通过连接错误将此转储提交给 MS (http://connect.microsoft.com)将是一个很好的开始。
我可以向您保证的是,GC 将做正确的事情来满足您的分配请求,这包括启动 GC 来清理旧垃圾以满足新的分配请求。
我不知道 Stackoverflow 上共享内存转储的政策是什么,但我很乐意看看以更多地了解您的问题。