在32位Windows可执行文件中使用/ LARGEADDRESSAWARE的缺点?

我们需要将其中一个可执行文件与这个标志链接起来,因为它使用了大量的内存
但为什么给一个EXE文件的特殊处理。 为什么不标准化/ LARGEADDRESSAWARE?

所以问题是:使用/ LARGEADDRESSAWARE有什么问题,即使你不需要它。 为什么不把它用作所有EXE文件的标准?

盲目地将LargeAddressAware标志应用于你的32位可执行文件, LargeAddressAware一个定时炸弹

通过设置这个标志, 正在作证的操作系统:

是的,我的应用程序(和运行时加载的所有DLL)可以处理高达4 GB的内存地址。
所以不要将进程的VAS限制为2 GB,而是解锁全部(4 GB)“。

但你能保证吗?
你是否承担你的流程可能使用的所有系统DLL,微软可再发行组件和第三方模块的责任?

通常情况下,内存分配以低到高的顺序返回虚拟地址。 所以,除非你的进程消耗了大量的内存(或者它有一个非常分散的虚拟地址空间),否则它将永远不会使用2GB边界以外的地址。 这是隐藏有关高地址的错误。

如果这样的错误存在,他们很难识别。 他们会零星地出现“迟早”。 这只是时间问题。

幸运的是,在Windows OS中内置了一个非常方便的系统级交换机:
出于测试目的使用MEM_TOP_DOWN注册表设置。
这迫使所有的内存分配从上到下,而不是正常的自下而上。

 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management] "AllocationPreference"=dword:00100000 

(这是十六进制0x100000,当然需要Windows重启)

通过启用此开关,您将可以“更快”地识别问题,而不是“以后”。 理想情况下,你会看到他们“从头开始”。

边注:首先分析我强烈推荐工具VMmapVMmap )。

结论:

将LAA标志应用于32位可执行文件时,必须在配有TopDown AllocationPreference开关的x64操作系统上进行完全测试。

对于您自己的代码中的问题, 可能可以修复它们。
仅举一个非常明显的例子:使用无符号整数代替内存指针的有符号整数。

当遇到与第三方模块有关的问题时,您需要请作者修复他的错误。 除非这样做,否则最好从可执行文件中删除LargeAddressAware标志。


测试说明:

MemTopDown注册表开关没有达到由“测试运行器”本身不是 LAA启用单元测试所需的结果。
请参阅: x86 LargeAddressAware兼容性的单元测试


PS:
也是非常“有关”的,而颇有意思的是从32位代码迁移到64位。
例如见:

因为大量的遗留代码是以期望“负”指针无效的方式编写的。 在32位进程的前两个Gb中的任何地方都有msb设置。

因此,微软更容易安全地使用它,并且要求(a)需要全部4Gb和(b)在大内存情况下开发和测试的应用程序,以简单地设置标志。

它没有 – 正如你所注意到的那样 – 很难。

Raymond Chen在他的博客“ The Old New Thing”中提到了所有(32位)应用程序的问题。

不,在这个上下文(C / C ++)中的“遗留代码”并不是专门用指针的MSB来玩丑陋技巧的代码。

它还包括所有使用'int'来存储两个指针之间的差异或存储区域长度的代码,而不是使用正确的类型'size_t':被签名的'int'具有31位,并且不能处理超过2Gb的值。

处理好你的代码的一个好办法就是去掉它,纠正所有这些无害的 “混合签名和未签名”警告。 它应该做好工作的一部分,至少如果你没有定义函数,其中int类型的参数实际上是一个内存长度。

然而,即使你没有任何改正,“遗留代码”可能会一段时间内正常工作。

只有当您在一个区块中分配超过2 Gb的数据时才会中断。 或者当你比较两个互不超过2Gb的无关指针时。
比较无关的指针在技术上是一个未定义的行为,你不会遇到那么多的代码(但你永远不能确定)。
而且,即使总共需要2Gb以上,您的程序实际上也不会使单个分配大于该分配。 事实上,在Windows中,即使使用LARGEADDRESSAWARE,在内存组织方式上也不能默认分配这么多。 你需要拖动系统DLL来获得超过2Gb的连续块

但是Murphy的定律说有一天会有一些代码会被打破,只是在没有检查的情况下启用LARGEADDRESSAWARE,以及什么时候没有人会记得这一切已经完成了。