【发布时间】:2014-10-29 17:55:41
【问题描述】:
我注意到我的应用程序内存不足的速度超出了预期。它创建了许多字节数组,每个数组都有几兆字节。但是,当我使用 vmmap 查看内存使用情况时,似乎 .NET 为每个缓冲区分配的内存远远超出了所需。准确地说,在分配 9 兆字节的缓冲区时,.NET 会创建一个 16 兆字节的堆。剩余的 7 兆字节不能用于创建另一个 9 兆字节的缓冲区,因此 .NET 会创建另外 16 兆字节。所以每个 9MB 的缓冲区浪费了 7MB 的地址空间!
这是一个在 32 位 .NET 4 中分配 106 个缓冲区后抛出 OutOfMemoryException 的示例程序:
using System.Collections.Generic;
namespace CSharpMemoryAllocationTest
{
class Program
{
static void Main(string[] args)
{
var buffers = new List<byte[]>();
for (int i = 0; i < 130; ++i)
{
buffers.Add(new byte[9 * 1000 * 1024]);
}
}
}
}
请注意,您可以将数组的大小增加到 16 * 1000 * 1024 并且在内存不足之前仍然分配相同数量的缓冲区。
VMMap 显示:
还要注意托管堆的总大小和提交的总大小之间几乎存在 100% 的差异。 (1737MB 与 946MB)。
在 .NET 上是否有解决此问题的可靠方法,即我是否可以强制运行时分配不超过我实际需要的内存,或者可能是可用于多个连续缓冲区的更大的托管堆?
【问题讨论】:
-
您对完全符合您需要的内存分配的渴望被一个更大的担忧所压倒,即避免地址空间碎片的需要。这种行为完全是设计使然,没有旋钮可以调整它。无论如何都是老式的问题,这在 64 位操作系统上不是问题。
-
@HansPassant 不幸的是,这个过程必须是 32 位的。所以你基本上是说我需要编写自己的分配器?
-
好吧,为什么不呢。或者你可以编写更智能的代码,需要更少的内存。重用这些数组。
-
@HansPassant。它们都包含实际需要的数据,因此除了将它们转储到磁盘之外,也没有什么可做的。
-
您可以将缓冲区分配为 7 个块(即
new byte[7 * 9 * 1000 * 1024]),这非常接近 64M。