如何在Array.Resize期间阻止OutOfMemory异常?

时间:2013-10-25 01:31:26

标签: c# arrays xna out-of-memory

我目前的项目包括植被模拟,能够渲染大量实例树模型,随着时间的推移逐渐增长和重现。

我目前在这行代码中获得了一致的OutOfMemory异常

if (treeInstances.Length <= currentIndex)
    Array.Resize(ref treeInstances, currentIndex + 500);

当模拟超出treeInstances数组的通常范围时,此代码运行,并使其分配一个新的数组,并为树提供额外的500个插槽。

鉴于我可以看到数组失败时的大小(通常介于3000到5000个实例之间)和TreeInstance结构的大小(20个浮点数),我确信我的问题不在于数组的原始大小。即使考虑到它必须在resize / 8进程期间暂时加倍(因为Array.Resize()分配一个新数组),假设我的数学运算正确,它仍然不到半个MB。

因此,我认为必定会有一些我遗漏的东西。是否有某些原因垃圾收集器可能无法删除旧数组?

更多详情:

  • TreeInstance是一个简单的结构,具有每棵树的变换矩阵和颜色。
  • treeInstances是一个TreeInstance[]数组。它仅在此处直接使用,在上面的代码行中。
  • treeInstances还有一个属性TreeInstances,可通过get;set;
  • 访问该属性
  • TreeInstances用于设置每棵树生长时的变换矩阵和颜色,并作为Draw例程的一部分提供给Instancing方法。
  • Instancing方法我不熟悉,但使用TreeInstances执行各种功能而不修改其内容(包括在DynamicVertexBuffer.SetData操作中将其用作源)。

2 个答案:

答案 0 :(得分:1)

C#旨在处理小内存分配比大内存分配好得多。当您执行Array.Resize时,您正在强制分配新的内存块,复制的数据然后旧块无效。这是一种非常有效的分段堆算法: - )

如果你知道你的数组在开始时需要多大,那就让你的数组大小。如果你不这样做,我建议你使用List或类似的类。该课程按每个项目分配。

我站起来纠正,谢谢你让我保持诚实。我太习惯于处理类而不是结构。我应该更清醒。

如果将TreeInstance更改为类,则List将成为地址数组,而TreeInstance可以/将以较小的块分配。新建所有TreeInstance都需要进行一些代码更改。

答案 1 :(得分:0)

看起来我找到了我的问题的答案,跟随Dweeberly对我的Array.Resize()系统的描述是“一个非常有效的分解堆的算法”。这是我的一个概念化问题:我不明白Out of Memory Exceptions可能是由于没有足够的连续内存引起的,而是假设我因为Garbage Collection没有捕获数组而达到某种限制。

Eric Lippert撰写的这篇博文让我直截了当:

http://blogs.msdn.com/b/ericlippert/archive/2009/06/08/out-of-memory-does-not-refer-to-physical-memory.aspx

对于任何处理Out of Memory Exceptions的人来说,或者作为游戏编程中任何人的一般知识都值得一读。

简短的回答是这样的:在为32位Windows编译的程序中,重复分配然后删除大对象,就像我通过Array.Resize()所做的那样,可以将地址空间分割成空白区域的“块”与您指定的对象一样大。随后,尝试分配大于任何这些空闲块的对象将引发Out Of Memory异常,即使您的累积内存也要大得多。

如上所述,适当的响应只是为了避免重复调整数组大小:我只需要理解为什么。在我的例子中,这意味着重写Instanced Model方法只绘制一个更大的数组的子集,而不是整个数组。之后,这是一种分配比初始化期间所需的更大阵列的简单方法。