3

我目前的项目包括一个植被模拟,能够渲染大量随时间生长和繁殖的树模型。

我目前在这行代码中遇到一致的 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还有一个 Property, TreeInstances,它通过get;set;
  • TreeInstances用于设置每棵树生长时的变换矩阵和颜色,并作为Draw例程的一部分输入到实例化方法中。
  • 我不太熟悉的 Instancing 方法,但在TreeInstances不修改其内容的情况下执行各种功能(包括将其用作DynamicVertexBuffer.SetData操作中的源)。
4

2 回答 2

1

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

如果您一开始就知道您的阵列需要多大,请将您的阵列设置为该大小。如果你不这样做,我建议你使用 List 或类似的类。该类按项目分配。

我的立场是正确的,谢谢你们让我诚实。我太习惯于处理类而不是结构。我应该更清醒一点。

如果将 TreeInstance 更改为类,则 List 变为地址数组,并且 TreeInstance 可以/将被分配在更小的块中。需要更改一些代码来新建所有 TreeInstance。

于 2013-10-25T01:45:05.410 回答
0

看起来我找到了我的问题的答案,遵循 Dweeberly 对我的Array.Resize()系统的描述,即“一种非常有效的碎片化堆算法”。这是我的一个概念化问题:我不明白内存不足异常可能是由于没有足够的连续内存引起的,而是假设我由于垃圾收集没有捕获数组而达到某种限制。

Eric Lippert 的这篇博文让我明白了:

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

非常值得任何处理内存不足异常的人阅读,或者作为游戏编程任何人的一般知识。

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

如上所述,适当的响应只是为了避免重复调整数组大小:我只需要了解原因。就我而言,这意味着重新编写实例化模型方法以仅绘制较大数组的子集,而不是整个数组。之后,这是一种分配比我在初始化期间可能需要的大得多的数组的简单方法。

于 2013-11-04T00:54:52.133 回答