“AnyCPU”平台目标上的内存分配模式

Mih*_*ert 7 .net c# memory

我故意在一个简单的 C# 程序中泄漏内存,以了解有关 .NET 如何管理这方面的更多信息。这是使用int[]数组完成的,每个数组的大小为 1000 万,每 100 毫秒声明一次。数组的元素不会被“触及”(如分配一个值),以免将数据带入进程的工作集中:

const int BlockSIZE = 10000000;  // 10 million
const int noOfBlocks = 500;
int[][] intArray = new int[noOfBlocks][];

for (int k = 0; k < noOfBlocks; k++) {
    intArray[k] = new int[BlockSIZE];
    Console.WriteLine("Allocated (but not touched) for array {0}: {1} bytes", k, BlockSIZE);
    System.Threading.Thread.Sleep(100);
}
Run Code Online (Sandbox Code Playgroud)

我正在使用 VMMap(Mark Russinovich 构建的工具)来查看内存是如何分配的。该版本是最新版本(3.25,2018 年发布),因此它了解托管堆。

在具有 8 GB RAM 的 x64 Windows 10 计算机上使用 Visual Studio 2015 来编译和生成文件.exe。根据Platform target项目“构建”部分中的设置,可以看到与如何分配内存相关的不同结果,如下所示。

Platform target设置为 时x86,提交的内存会增长直至接近 2 GB 标记,然后抛出内存不足错误。该值是预期的,因为 2 GB 是 x86 架构上用户虚拟地址空间的限制(我没有使用 IncreaseUserVA,这会使此值达到 3 GB稍后编辑:这并不完全正确 - 请参阅 David 的答案如下)。在这种情况下,VMMap 的输出如下。正如预期的那样,大多数提交的数据都属于托管堆类别。

平台目标= x64

Platform target设置为 时x64,承诺区域将按预期持续增长。最终该应用程序需要被终止,因为它不断分配内存。这也在意料之中,因为只要可用 RAM + 分页文件的总量能够适应增长,64 位 Win10 机器的理论限制是每个用户虚拟地址空间 128 TB(受当前处理器的限制)因为它们只使用虚拟地址中可用的 64 位中的 48 位)。VMMap 的输出如下。同样,大多数提交的字节都属于托管堆类别。

在此输入图像描述

Platform target设置为Any CPUPrefer 32-bit勾选 时(这实际上是 Visual Studio 2015 中的默认设置),结果并不那么简单。首先,当提交的内存达到大约 3.5 GB 时,会抛出内存不足异常。其次,托管堆中的私有字节仅增长到约 1.2 GB,之后私有数据类别将注册接下来要分配的数据。VMMap 的输出如下。

在此输入图像描述

为什么分配会发生如最后一段中所述的Any CPU+Prefer 32-bit设置?具体来说,为什么大量数据列在“私有数据”而不是“托管堆”下?

稍后编辑:添加内联图片以获得更好的清晰度。

Dav*_*oft 5

LARGEADDRESSAWARE 在 Windows64 (wow64) 上运行的 Windows 下的 32 位进程具有 4GB 用户模式虚拟地址空间 (VAS),因为内核内存是 64 位,并且不需要映射到使用 32 位指针可寻址的 4GB。而且您不必使用启动 Windows 即可/3GB switch获得它。

针对 X86 进行编译时,您可能期望在 32 位和 64 位平台上有相同的行为,因此不设置 LARGEADDRESSAWARE 标志是有意义的。这也可能是向后兼容性所迫。在过去,一些 32 位库(错误)使用了指针的高位,因此历史上将 32 位程序限制为 2GB 是一种安全设置。

AnyCPU+Prefer 32 位是较新的设置,默认设置为 LARGEADDRESSAWARE,以便您更好地访问 64 位平台上的资源。