c# - 'AnyCPU' 平台目标上的内存分配模式

标签 c# .net 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);
}

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

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

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

Platform target= x64

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

enter image description here

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

enter image description here

为什么分配发生在最后一段中描述的 Any CPU + Prefer 32-bit 设置?具体来说,为什么大量数据列在 Private Data 而不是 Managed Heap 下?

后期编辑:添加内联图片以提高清晰度。

最佳答案

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

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

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

关于c# - 'AnyCPU' 平台目标上的内存分配模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54543938/

相关文章:

.net - 在 System.AddIn 中托管 WPF 时出现 NullReferenceException

c - 使用/dev/kmem 从内核读取地址值

C 编程 memset arpa.inet

IOSurface 在 iOS 12 及以上版本逐渐增加内存

c# - 使用非参数化查询避免 SQL 注入(inject)

c# - 对 DI、IoC、Unity 和 Moq 的类型注册和实现感到困惑

c# - 该应用程序要求首先在全局程序集缓存 (GAC) 中安装程序集 Microsoft.ReportViewer.WinForms 版本 11.0.0.0

c# - 在 Azure 辅助角色中读取/写入临时文件

c# - 在 C#/.NET 4.0 中填充和使用动态类

c# - 打印缩放我的 WPF-GroupBox