我故意在一个简单的C#程序中泄漏内存,以了解有关.NET如何管理此方面的更多信息。这是使用int[]
数组来完成的,每个数组的大小为1000万,每100ms声明一次。为了不将数据带入流程的工作集中,未“触摸”数组的元素(如分配值一样):
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);
}
我正在使用VMMap(由Mark Russinovich构建的工具)来查看如何分配内存。该版本是最新版本(3.25,于2018年发布),因此它了解托管堆。
Visual Studio 2015在具有8 GB RAM的x64 Windows 10计算机上使用,以编译和生成.exe
文件。根据项目“构建”部分中的Platform target
设置,可以看到与内存分配方式有关的不同结果,如下所示。
将Platform target
设置为x86
时,在抛出内存不足错误之前,已提交的内存会增长到接近2 GB的标记。该值是可以预期的,因为x86体系结构上的用户虚拟地址空间限制为2 GB(我没有使用GrowthUserVA,这会使该空间最多增加3 GB, 稍后编辑 :这并不完全正确-请参阅下面的David回答)。在这种情况下,VMMap的输出如下。正如预期的那样,大多数已提交数据都属于“托管堆”类别。
将Platform target
设置为x64
时,承诺区域将继续增长,这与预期的一样。最终,由于该应用程序不断分配内存,因此需要将其终止。这也是可以预料的,因为只要可用的ram +页面文件的总数可以容纳增长,则64位Win10盒的理论限制是每个用户虚拟地址空间128 TB(受当前处理器的限制)因为它们仅使用虚拟地址中可用的64位中的48位)。 VMMap的输出如下。同样,大多数已提交的字节都属于“托管堆”类别。
当Platform target
设置为Any CPU
并勾选了Prefer 32-bit
时-这实际上是Visual Studio 2015中的默认设置-结果不是那么简单。 首先,当提交的内存达到约3.5 GB时,将引发内存不足异常。 第二,托管堆中的专用字节仅增长到约1.2 GB,之后,“专用数据”类别将注册接下来要分配的数据。 VMMap的输出如下。
为什么如上段所述为Any CPU
+ Prefer 32-bit
设置进行分配?具体来说,为什么在“私有数据”下而不是“托管堆”下列出大量数据?
稍后编辑:内联添加图片以提高清晰度。
答案 0 :(得分:3)
LARGEADDRESSAWARE 32位进程在Windows下运行于Windows64(wow64)上,具有4GB的用户模式虚拟地址空间(VAS),因为内核内存为64bit,不需要映射到可使用32bit指针寻址的4GB。而且您不必使用/3GB switch
来启动Windows即可。
针对X86进行编译时,您可能会期望在32位和64位平台上具有相同的行为,因此不要设置LARGEADDRESSAWARE标志是有意义的。同样,这可能是由于向后兼容所致。在非常时代,一些32位库(误用)使用了高位指针,因此从历史上讲,将32位程序限制为2GB是一种安全设置。
AnyCPU +首选32位是一个较新的设置,默认情况下设置为LARGEADDRESSAWARE,以便您可以更好地访问64位平台上的资源。