如何创建一个(32位).NET应用程序来使用3GB RAM?

12

我正在创建一个使用大量RAM的.NET应用程序(C#)。我最近了解到,在32位版本的Windows XP上,我只能使用2GB,除非我使用/3Gb开关,并在可执行文件头中设置IMAGE_FILE_LARGE_ADDRESS_AWARE标志。但是,由于我正在开发一个.NET应用程序,我想我不能直接修改可执行文件,对吗?那么,我该怎么做才能允许我的应用程序利用3GB呢?


如果你需要更快的速度和更高的内存效率,也许你可以将矩阵运算和数字计算移植到本地的C++ dll中? - Spence
我打算最终这样做,但需要时间。在那之前,我必须使用我的现有代码库,它是用C#编写的。 - Hosam Aly
7个回答

13
一个.NET exe仍然是一个标准的PE文件;因此,您可以尝试使用editbin /LARGEADDRESSAWARE来设置标志,但请注意,如果您使用类似ClickOnce的东西(因为它会维护文件的加密哈希),这样做将不起作用。
但请注意,在单个对象/数组的最大大小方面,您仍将面临相同的.NET限制。对于大量内存,x64是一个更好的选择。

谢谢。我不知道它是标准PE!所以它只是加载JIT来编译存储在可执行文件中的实际字节码?我的理解正确吗? - Hosam Aly
1
(好吧,严格来说它只是要求CLI接管,然后那个执行JIT,但足够接近了;-p) - Marc Gravell
++ 对于 x64 的 props,绝对是解决整个 3GB 问题的好方法,而且由于美妙的 .net,您无需重新编译。 - Spence
谢谢Marc。 :) 我不能使用64位,因为我们没有64位许可证(适用于Windows和第三方应用程序)。希望我们能尽快解决这个问题。 - Hosam Aly
1
这个方法适用于我在Windows 7(64位)上使用的一个为x86编译的.EXE文件;我能够分配给我的exe的总内存从1.77 GB增加到了3.68 GB。 - BrainSlugs83

6
/3GB开关位于操作系统的引导程序中,而不是应用程序中。 (编辑:它也存在于本机C / C ++编译器中,但不在C#编译器中)就您的应用程序而言,它将请求内存,操作系统将将其提供给您的进程。 但是,在您的程序使用虚拟内存之前,您可以访问1 GB以上的内存(可能性,根据硬件外围设备的不同,您并非总是获得3 GB)。
正如Marc Gavell向我指出的那样,您可能需要在exe上运行命令“editbin /LARGEADDRESSAWARE my.exe”作为后期构建选项以启用此功能。在这里找到了一个有关MS人员谈论它的参考资料:MS论坛 我建议您查看您的程序,看看是否可以重新设计它以使用更少的内存。也许您可以将数据集分成较小的块来处理,而不是一次性将整个数据集加载到内存中?

2
谢谢。该应用程序是专门为了尽可能快地进行数字计算和矩阵操作而构建,需要消耗尽可能多的内存。但从您的回答中得到的信息是,CLR 设置了 IMAGE_FILE_LARGE_ADDRESS_AWARE 位吗? - Hosam Aly
@Hosam - 你可以随时使用 dumpbin /HEADERS 命令来查找你的exe文件;我刚刚检查了一下,至少在我的测试中它默认不会这样做。 - Marc Gravell
1
@Spence - 不,你还需要设置exe级别的头标志。 - Marc Gravell
这个没有编译器标志:http://msdn.microsoft.com/en-us/library/6ds95cz0(VS.80).aspx在混合模式exe中,你肯定需要它,但是你确定对于一个C# exe吗? - Spence
@Marc:谢谢你的提示。了解更多工具总是好的。 :) - Hosam Aly
1
@Spence - 嗯,我的理解是它更适用于进程(通过操作系统)而不是实现。我可能错了...我想我们可以通过强制进行大量内存加载等测试来验证。 - Marc Gravell

1

谢谢。这是我第一次知道这个函数。必须调用它吗?还是为了以防万一,以免其他应用程序占用我的内存?(如果机器专用于我的应用程序,我需要它吗?) - Hosam Aly
如果你不在那时调用它,当你超过最大工作集时,操作系统将开始通过将内存分页到页面文件来“修剪”你的工作集(这会影响程序和操作系统的性能,因为需要在磁盘和内存之间交换内存)。所以,如果你想要...,你应该调用它。 - ChrisW
...实际上会使用所有这些内存。或者,如果你可以接受使用3GB的虚拟内存(而不是实际的内存,即实际的RAM),那么你就不需要调用它。 - ChrisW
那我肯定想要调用它。:) 非常感谢! - Hosam Aly

0

嗯,我不确定这个问题,但这是我认为的:

.NET可执行文件可以通过两种方式编译 - 平台特定和平台无关。默认情况下,它们是平台无关的,当运行程序时,代码会(如其他答案中所述)JIT到平台特定的代码。

现在,例如,如果您的可执行文件是这些平台无关的之一,并且在64位操作系统上运行它,它将被JIT到64位代码,对吧?因此,它将能够访问超过3GB的RAM。

我的意思是 - 我认为PE头中写了什么并不重要。实际可用RAM的数量由.NET运行时确定,后者又查看当前平台并生成最佳的JIT代码。

我认为您不应该担心/3GB开关,因为.NET会为您处理它。相信.NET! :)


那是如果我在64位操作系统上运行它,但我目前无法这样做。在32位操作系统上,我需要设置large-address-aware标志。 - Hosam Aly
对于传统的可执行文件来说,是的。但.NET可执行文件并不直接由Windows执行!它们首先会被.NET运行时转换,从而生成完全不同的内容!运行的不是你的.exe文件,而是.NET生成的那个文件。 - Vilx-
所以如果必须设置标志,则必须在 .NET 生成的 .exe 上设置,而不是在您的 .exe 上设置! - Vilx-
你的 .net exe 实际上是一个真正的带有 PE 头的 exe,就像 Marc Gravell 指出的那样。这段代码加载并调用 Cor_BindToRuntimeEx() 函数,该函数会加载 CLR。然后,CLR 跳转到 EXE 中 .net 代码的第一个点并开始执行。因此,Windows 就像运行 C++ exe 一样运行你的 exe :(. - Spence

0

你可以尝试使用命名管道进行远程调用,并通过物理上拥有更多进程来获得更多内存。

如果你正在进行任何形式的Interop(普通的.Net套接字也算在内),你应该创建一个对象缓存(例如,使用套接字的byte[]缓冲区),在应用程序启动时分配大量这些对象。

你应该阅读this article


谢谢,但远程调用会给我的应用程序带来很大的开销。 - Hosam Aly
你可能会感到惊讶。使用命名管道(它们是IPC通道),您可以获得相当不错的性能。+请注意编辑。 - Jonathan C Dickinson

0

我刚在 Windows 10 (64位) 上用 VS2019 测试了一个小的 x86 C# 程序。

默认情况下,在它耗尽内存之前,我可以分配 1605 个大小为 1Mb 的字符串。

通过设置:

"editbin /LARGEADDRESSAWARE my.exe"

我可以分配 3330 个大小为 1Mb 的字符串,在它耗尽内存之前。

在 Windows 10 (64位) 上,因此可以寻址超过 3Gb 的空间。


-1
据我所知,/3GB开关只能用于Windows Server(2000或2003),而不适用于Windows XP。您需要在boot.ini文件的末尾编写/3GB。这样,对于应用程序,操作系统会启用更多内存,以便正常分配给内核进程使用。但是,/3GB并不意味着您可以为应用程序使用3GB的内存,它只能使用更多的内存,但不一定是3GB。对于.net应用程序,再次据我所记,使用/3GB开关最多可使用1.8GB的内存。顺便说一下,您还可能想检查/PAE开关。

谢谢。根据MSDN(http://msdn.microsoft.com/en-us/library/bb613473.aspx)的说法,switch在Windows XP Professional上得到支持。至于.NET限制,为什么只限制在1.8呢?那太少了!(顺便说一下,我不是在谈论连续数组。) - Hosam Aly
我的知识主要基于3-4年前使用 .net 1.1 的经验。我不确切知道主要原因,即使我知道现在也记不起来了。但是,每当我遇到OOM异常时,转储文件显示使用总内存为1.8GB,在/3GB开关之前为800MB。 - hakan
@haken - 听起来可能与 LOH 的碎片化或尝试分配单个大型数组等有关。只是一些想法。 - Marc Gravell
我想,也许我当时检查的是已提交的字节而不是保留的字节,但这仍然无法解释巨大的差异。我们没有那么大的LO或类似的东西。但最好不要对我不太记得的事情进行猜测。 - hakan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接