我遇到一个问题,几个 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),由于业绩。

如果你这样做常常分配和释放的大型物体,它将使罗的碎片,即使你有更多的免费存储在总比你需要什么,你不可以有一个连续的存储空间了,因此,将获得内存例外。

我可以想两种解决方法在这个时刻。

  1. 移动到64位的机器/操作系统,并利用它:)(最简单的,但可能难以及取决于资源的限制)
  2. 如果你无法做到#1,然后再分配一个巨大的吸盘存储器的第一并使用他们的(可能需要编写一些辅助类操纵的一个小型阵列,这实际上属于一种大阵列),以避免碎片。这可以帮助一点,然而,它不可能完全解决的问题和可能必须处理的复杂性。

其他提示

看来你碰到的蕙(大对象堆)碎片问题。

大对象堆

CLR内而外大对象堆揭秘

您可以检查看看,如果你有使用SOS蕙碎片问题

检查此问题获得如何使用SOS来检查LOH的例子。

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

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 上共享内存转储的政策是什么,但我很乐意看看以更多地了解您的问题。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top