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

Posted

技术标签:

【中文标题】在 32 位 .NET 进程中分配超过 1,000 MB 的内存【英文标题】:Allocating more than 1,000 MB of memory in 32-bit .NET process 【发布时间】:2010-11-09 17:19:14 【问题描述】:

我想知道为什么我不能在我的 32 位 .NET 进程中分配超过 1,000 MB 的内存。以下迷你应用程序在分配 1,000 MB 后引发 OutOfMemoryException。为什么是 1,000 MB,而不是 1.8 GB?是否可以更改整个流程的设置?

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:垃圾收集没有帮助。

编辑,澄清我想要什么:我编写了一个服务器应用程序,它在写入数据库/磁盘之前处理大量数据。我没有为所有内容创建临时文件,而是编写了一个内存缓存,这使得整个事情变得超级快。但是内存是有限的,所以我试图找出限制是什么。并且想知道为什么我的小测试程序在正好 1000 MB 之后抛出 OutOfMemoryException。

【问题讨论】:

【参考方案1】:

拥有巨大的内存块绝不是一个好主意,即使在 64 位中也是如此。您会遇到连续内存和碎片的大问题。

这里的问题是找到一个连续的块。您可以尝试启用 3gb 模式(这可能有助于它找到更多字节)但我真的建议不要这样做。这里的答案是:

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

您可能还想阅读Eric Lippert's 博客(他似乎对每个常见的 .NET 问题都有一个博客条目......)

【讨论】:

Johannes,为什么拥有更多地址空间不会更容易找到更多连续的地址空间?我完全被你的评论弄糊涂了。你能解释一下吗?【参考方案2】:

Win32 进程的虚拟地址空间限制为 1.5GB(不完全正确)。此外,在 .NET 框架中,对 .NET 进程可以消耗的内存百分比有一个限制。 machine.config 有一个 processModel 元素,其属性 memoryLimit 是进程可以消耗的可用内存的百分比。默认值为 60%。

如果您正在运行的机器有 2GB 内存,或者您没有在 BOOT.INI 中启用 /3GB 开关,那么每个进程将获得 ~1.3GB 内存。

我找不到知识库文章,但如果我没记错的话,无论您的设置如何,.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

【讨论】:

谢谢,这给了我一个想法。但是,这些链接并没有真正的帮助。 只有服务器 CLR 使用 memoryLimit 配置。工作站 CLR 始终使用尽可能多的内存。 @ezolotko - 我从来不知道有什么不同。【参考方案3】:

我最近在 .NET 中对 32 位进程的内存限制进行了广泛的分析。我们都被我们可以在 .NET 应用程序中分配多达 2.4GB (2^31) 的想法轰炸了,但不幸的是,这不是真的 :(。应用程序进程有这么多的空间可以使用,并且操作系统做得很好然而,为我们管理它的工作,.NET 本身似乎有自己的开销,对于推动内存限制的典型现实世界应用程序来说,这大约占 600-800MB。这意味着,一旦您分配一个整数数组,大约需要1.4GB,您应该会看到 OutOfMemoryException()。

显然,在 64 位中,这个限制发生得更晚(让我们在 5 年后聊聊 :)),但由于字长增加,内存中所有内容的一般大小也会增长(我发现它是 ~1.7 到 ~2 倍) .

我可以肯定的是,操作系统的虚拟内存理念绝对不会在一个进程中为您提供几乎无限的分配空间。只有这样才能将全部 2.4GB 的空间分配给同时运行的所有(许多)应用程序。

我希望这种见解有所帮助。

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

Is there a memory limit for a single .NET process

【讨论】:

【参考方案4】:

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

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

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

在 64 位 Windows 操作系统上使用 64 位构建的应用程序将允许您的程序分配超过 ~2GB 的内存,大概最多 2^64 个地址空间(如果您有 RAM 和磁盘空间可用,这是截至撰写本回复时的真正限制因素)。

如果您的应用程序内存仍然不足,您还可以增加 Windows 内存页面文件的大小。在 Windows 上,页面文件允许操作系统在 RAM 内存空间不足时将内存从 RAM 转移到磁盘。但是将部分 RAM 内存移入和移出磁盘会花费大量时间,因此它可能会真正影响应用程序的性能。无论性能如何,通过增加页面大小,您可以(理论上)使页面文件与 Windows 机器的 C: 驱动器上的可用空间一样大。在这种情况下,您的应用程序将能够在程序执行期间分配,例如,最多 4 TB 的内存(或您的页面文件大小设置的任何内存量)。要更改 Windows 机器的页面文件设置,请执行以下操作:

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

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

【讨论】:

【参考方案5】:

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

【讨论】:

【参考方案6】:

我认为这里的问题是这个应用程序会在它进行的每个循环中添加 10MB,并且循环是:“while(true)”,这意味着它将添加这 10MB 直到应用程序停止。因此,如果它运行 100 次循环,它会增加近 1GB 的 RAM,我假设它会在不到 30 秒内完成此操作。我的意思是你试图在一个永无止境的循环中每个循环使用 10 兆字节的内存

【讨论】:

【参考方案7】:

如果我没有明白你的意思,我真的很抱歉,但是:

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);
        
    

你试过使用方法吗?这可能是一个愚蠢的问题,但你为什么要创建一个永恒的循环? O 如果您尝试代码剥离符号 >.> xD.

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

【讨论】:

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

以上是关于在 32 位 .NET 进程中分配超过 1,000 MB 的内存的主要内容,如果未能解决你的问题,请参考以下文章

.STACK 没有在 MASM 中分配正确的大小

是否可以在linux中分配大量虚拟内存?

如何在C#中分配不应该用Int32转换为1的01,并在引用时给出01

如果您在进程崩溃后在进程中分配内存会发生啥?

如何在C中的堆栈中分配超过所需的内存?

如何在 C 中分配和释放对齐的内存