在32位的.NET进程中分配超过1,000 MB的内存

21

我想知道为什么我的32位.NET进程不能分配超过1000MB的内存。以下小应用程序在分配了1000MB后会抛出OutOfMemoryException异常。为什么是1000MB而不是1.8GB?是否有一种可以更改进程范围设置的方法?

static void Main(string[] args)
{
    ArrayList list = new ArrayList();
    int i = 0;
    while (true)
    {
        list.Add(new byte[1024 * 1024 * 10]); // 10 MB
        i += 10;
        Console.WriteLine(i);
    }
}

PS:垃圾回收并不能帮助解决问题。

编辑,为了澄清我的需求:我编写了一个服务器应用程序,处理大量的数据之后再将其写入数据库/磁盘。我没有为每个东西创建临时文件,而是编写了一个内存缓存,使得整个处理过程超级快速。但是内存是有限的,所以我尝试找出限制是什么。我想知道为什么我的小测试程序在恰好达到1,000 MB 后就抛出 OutOfMemoryException 异常。

7个回答

18

即使在64位系统中,拥有大块内存也不是一个好主意。连续内存和碎片化会带来很大的问题。

问题在于找到一个连续的内存块。你可以尝试启用3gb模式(可能会帮助找到更多字节),但我真的不建议这样做。以下是解决方案:

  • 使用更少的内存
  • 使用数据库/文件系统
  • 使用x64

您还可以阅读Eric Lippert的博客(他似乎为每个常见的.NET问题都有一篇博客文章...)


2
Johannes,为什么拥有更多的地址空间不会使查找更多连续地址空间变得更容易?我完全被你的评论搞糊涂了。你能解释一下吗? - Eric Lippert

7
一个Win32进程的虚拟地址空间限制为1.5GB(不完全准确)。此外,在.NET框架中,有一个限制.NET进程可消耗内存百分比的限制器。 machine.config具有processModel元素和一个memoryLimit属性,该属性是进程可以消耗的可用内存的百分比。默认值为60%。
如果您运行的机器具有2GB的内存或者您没有在BOOT.INI中启用/3GB开关,则每个进程将获得约1.3GB的内存。
我找不到KB文章,但如果我记得正确,无论您的设置如何,.NET 1.x都无法超出1.5GB(1.8GB?)的限制。

http://blogs.msdn.com/tmarq/archive/2007/06/25/some-history-on-the-asp-net-cache-memory-limits.aspx http://social.msdn.microsoft.com/Forums/en-US/clr/thread/c50ea343-b41b-467d-a457-c5a735e4dfff http://www.guidanceshare.com/wiki/ASP.NET_1.1_Performance_Guidelines_-_Caching#Configure_the_Memory_Limit


1
谢谢,这给了我一个想法。然而,这些链接并不是真正有用的。 - Stefan Schultze
只有服务器CLR使用memoryLimit配置。工作站CLR总是尽可能使用尽可能多的内存。 - ezolotko
@ezolotko - 我从来不知道有什么区别。 - MyItchyChin

4
我最近在32位进程中进行了大量有关.NET内存限制的分析。我们都被灌输了一个想法,即在.NET应用程序中可以分配高达2.4GB(2^31)的空间,但不幸的是这并不是真的:(。应用程序进程可以使用那么多的空间,并且操作系统为我们管理得非常好,但是,.NET本身似乎有自己的开销,这占了典型实际应用程序中将内存限制推到极致时约600-800MB的空间。这意味着,一旦您分配了一个占用约1.4GB的整数数组,您应该期望看到一个OutOfMemoryException()。
显然,在64位中,这个限制发生得晚得多(让我们在5年后再聊:)),但是由于增加了字长,内存中所有东西的一般大小也增加了(我发现大约是1.7到2倍)。
我确定的是,操作系统的虚拟内存概念绝对不会在一个进程中给您提供几乎无限的分配空间。它只是存在,以便所有(许多)同时运行的应用程序都可以寻址完整的2.4GB。
我希望这个见解能在某种程度上有所帮助。

我最初在这里回答了一些相关的问题(我还是一个新手,所以不确定我应该如何做这些链接):

单个.NET进程是否有内存限制


0
将程序编译为 Any CPU,您将拥有无限量的内存可供使用,并且程序仍然可以使用 x86(32 位)DLL 导入!!!

0
你可以通过将应用程序构建为64位架构来分配比 ~2 GB 更多的内存,这需要在Visual Studio中创建一个新的构建配置,并且该应用程序的构建仅在64位Windows版本上运行。在.NET中,对于应用程序使用默认的“任何CPU”构建选项,我发现即使在64位Windows机器上,我只能从堆中分配约1.5 GB的内存,这是因为应用程序实际上只在“任何CPU”模式下构建时以32位模式运行。但是通过编译为x64架构,您可以在执行应用程序期间从堆中分配更多的内存,我将解释如何为您的应用程序创建x64构建:

如果在.NET项目中使用普通(默认)的“Any CPU”构建选项,则你的应用程序将始终在32位模式下运行,即使在64位Windows操作系统上也是如此。因此,在应用程序执行期间,你将无法分配超过大约1.5到2 GB的RAM内存。要以真正的64位模式运行.NET应用程序,你需要进入构建配置管理器,并为x64架构创建一个构建类型,然后明确地使用该构建类型重新编译程序,以便进行x64编译。可以使用以下步骤为.NET解决方案创建x64构建模式选项:

在Visual Studio的“解决方案资源管理器”窗格中,右键单击解决方案图标,从弹出菜单中选择“配置管理器”选项。这将为.NET解决方案文件打开构建“配置管理器”对话框窗口。
在构建“配置管理器”对话框的右上方,单击下箭头并选择“<new>”选项。这将打开“新建解决方案平台”对话框。
在“新建解决方案平台”对话框中,对于“平台”选项,请从下拉菜单中选择“x64”。然后单击“确定”按钮,新的x64构建选项现在将在配置管理器对话框中可用。
然后,在“配置管理器”对话框中,在“活动解决方案平台”下拉菜单中选择“x64”。然后单击“关闭”按钮。
在Visual Studio的“解决方案资源管理器”窗格中,右键单击CS项目图标,并从弹出菜单中选择“属性”选项(此菜单底部的最后一个选项)。这将打开CS项目属性窗口。
在CS项目属性窗口的左侧,单击“生成”选项卡以显示代码项目的生成属性。在此窗口的顶部,请注意“平台”现在应该说“x64”(而不是默认的“任何CPU”选项)。如果“平台”下拉菜单没有显示“x64”,则现在应该选择它。
然后只需构建您的代码,在“bin”文件夹中,您现在应该有一个包含新的64位应用程序构建的x64文件夹。

在64位Windows操作系统上使用64位版本的应用程序将允许您的程序分配比约2GB更多的内存,假设您有可用的RAM和磁盘空间(这是撰写此响应时的真正限制因素),理论上可以达到2^64地址空间。

如果您的应用程序仍然耗尽内存,您还可以增加Windows内存页面文件的大小。在Windows上,页面文件允许操作系统将内存从RAM转移到磁盘,如果它的RAM内存空间不足。但是,在将RAM内存部分从磁盘中移动回来的过程中,会有很大的时间成本,因此可能会对应用程序的性能产生实际影响。无论性能如何,通过增加页面大小,您可以(理论上)使页面文件的大小与Windows机器C:驱动器上的可用空间一样大。在这种情况下,您的应用程序将能够在执行程序期间分配例如高达4TB的内存(或者您的页面文件大小设置为任意数量的内存)。要更改Windows机器的页面文件设置,请执行以下操作:

  1. 右键单击“This PC”,选择弹出菜单中的“属性”选项,打开“系统属性”对话框。在后续版本的Windows(Windows 10、Win 2012 Server等)中,也可以通过“开始”>“控制面板”>“系统和安全”>“系统”来实现。
  2. 在“系统”对话框的左侧,单击“高级系统设置”选项。这将显示Windows遗留的“系统属性”对话框的“高级”选项卡。
  3. 在“系统属性”对话框的“高级”选项卡中,单击“性能”框中的“设置”按钮。这将打开“性能选项”对话框。
  4. 在“性能选项”对话框中,单击“高级”选项卡,以查看Windows内存页面文件的当前大小设置。
  5. 要增加页面文件大小,请单击“更改”按钮,将打开“虚拟内存”对话框。
  6. 在“虚拟内存”对话框中,选择“C:”驱动器,然后在“自定义大小”下设置“初始”和“最大”大小。您可以使用任何大小,但不能超过C:驱动器上的可用空间,但是此更改将为硬盘上的页面文件保留该空间。
  7. 然后单击所有对话框上的“确定”以提交新设置。然后重新启动计算机,以确保所有更改已正确完成,并且新的页面文件设置正在运行。

无论如何,我希望这可以帮助人们理解为什么他们在运行 .NET 应用程序时,即使在 64 位 Windows 机器上运行,也可能遇到 1.5-2 GB 内存限制问题。这对人们来说可能是一个非常令人困惑的问题,我希望我的解释是有意义的。如果需要,欢迎随时向我发送有关此答案的问题。


-1

我认为这里的问题是每次循环时该应用程序将添加10MB,而循环是“while(true)”,这意味着它会一直添加这些10MB,直到应用程序停止。因此,如果它运行100个循环,它将向RAM中添加接近1GB,并且我假设它会在不到30秒的时间内完成。我的观点是您正在尝试在永无止境的循环中使用10兆字节的内存。


-2

如果我没有理解你的意思,我真心抱歉:

static void Main(string[] args)
{
    ArrayList list = new ArrayList();
    int i = 0;
    while (true)
    {
        using(byte newBt = new byte[1024 * 1024 * 10])
        {
            list.Add(newBt); // 10 MB
            i += 10;
            Console.WriteLine(i);
        }
    }
}

你尝试过使用这个方法吗?然后这可能是一个愚蠢的问题,但你为什么创建了一个无限循环?哦如果你尝试代码去除符号>.> xD。

来源:http://msdn.microsoft.com/en-us/library/yh598w02(v=vs.80).aspx


1
我已经编辑了你的答案以修复代码格式。下次需要发布代码时,请将其粘贴,选择它并按CTRL+K或{}图标,它将自动格式化。 - BlackBear
这完全错过了问题的重点。无限循环分配内存直到失败,从而确定进程可以消耗的最大内存量。 你的代码无法编译,因为它试图将字节数组分配给一个字节。此外,这两个都没有实现IDisposable。如果它在每次迭代中释放内存,它将破坏测试(它最终会报告约(2 ^ 31)/ 10,但该数字与其可以分配的内存量无关)。 - John B. Lambe

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