您应该能够分配多少内存?
Posted
技术标签:
【中文标题】您应该能够分配多少内存?【英文标题】:How much memory should you be able to allocate? 【发布时间】:2010-11-05 06:20:37 【问题描述】:背景:我正在编写一个处理大量地理数据的 C++ 程序,并希望一次加载大块以进行处理。我只能使用为 32 位机器编译的应用程序。我正在测试的机器运行的是 64 位操作系统(Windows 7)并且有 6 gig 的内存。使用 MS VS 2008。
我有以下代码:
byte* pTempBuffer2[3];
try
//size_t nBufSize = nBandBytes*m_nBandCount;
pTempBuffer2[0] = new byte[nBandBytes];
pTempBuffer2[1] = new byte[nBandBytes];
pTempBuffer2[2] = new byte[nBandBytes];
catch (std::bad_alloc)
// If we didn't get the memory just don't buffer and we will get data one
// piece at a time.
return;
我希望能够在应用程序达到 32 位寻址的 4 GB 限制之前分配内存。但是,当 nBandBytes 为 466,560,000 时,新的在第二次尝试时会抛出 std::bad_alloc。在这个阶段,进程的工作集(内存)值是 665,232 K 所以,我似乎无法获得甚至分配的内存。
有人提到 32 位 Windows 中的应用程序限制为 2 gig,使用 win32 的 /3GB 开关可以将其扩展到 3 gig。这是在那种环境下的好建议,但与本案无关。
在 64 位操作系统和 32 位应用程序下,
【问题讨论】:
我在网上找到了这个参考:“如果您在 64 位操作系统上作为 32 位应用程序运行,那么您将获得所有 4G 地址空间,所有这些都可以通过物理内存(如果你有 RAM),即使你自己没有使用 64 位指针。”来自博客:blogs.msdn.com/ricom/archive/2009/06/10/… 在我的 32 位机器上,我可以在简单的测试中分配 466,560,000×3 字节。在您的情况下,似乎是进程内存在分配点已经碎片化。 我很难选择一个答案来标记这个问题的正确性。我相信答案很复杂,取决于许多因素。内存映射文件是一个很好的答案,但这个问题的根本原因似乎是内存碎片。 bke1 指出了查看内存的好工具,很多人谈到内存碎片,但我选择了第一个明确说明问题并给出硬限制的答案(64 位下的 4 Gig 和正确的标志。) 感谢所有人,特别感谢提供优秀文章的链接。 进一步的测试揭示了这一点:我尝试将这个 ram 分配为 3000 件,但它在大约 95% 的地方失败了。比分 3 件更接近完成,但仍然没有运气。 VMMap 工具报告说我有 1.4 Gig 的可用空间,但仍然有 3000 块我无法分配 1.3 Gig。继续解决这个问题,我将尝试内存映射文件。 【参考方案1】:操作系统想给你多少。默认情况下,Windows 允许 32 位进程拥有 2GB 的地址空间。这被分成几个块。为堆栈留出一个区域,为每个加载的可执行文件和 dll 留出其他区域。剩下的任何东西都可以动态分配,但不能保证它会是一个连续的大块。它可能是几个较小的块,每个几百 MB。
如果您使用 LargeAddressAware 标志进行编译,64 位 Windows 将允许您使用完整的 4GB 地址空间,这应该会有所帮助,但总的来说,
您不应假定可用内存是连续的。您应该能够处理多个较小的分配,而不是几个大的分配,并且 如果您需要大量内存,则应将其编译为 64 位应用程序。【讨论】:
"你不应该假设可用内存是连续的。你应该能够处理多个较小的分配而不是几个大的分配,并且"这是错误的,因为 windows 使用分页内存并且应用程序对于一大块是连续的 Lodle,你混淆了地址空间和空闲地址空间。例如。最常见的碎片形式之一是由您的代码本身引起的。 EXE 和 DLL 不会从 0x00000000 开始加载。 @Lodle:不,应用程序看到一个连续的地址空间,但 exe、dll、堆栈和静态变量都加载到该地址空间内的不同地址。 连续的地址空间称为虚拟内存。在将虚拟地址转换为物理地址的处理器旁边有一个硬件组件,即内存管理器。你的程序被编译成目标代码 wrt 一个虚拟地址空间。【参考方案2】:在 windows 32 位上,正常进程最大占用 2 GB,但使用/3GB 开关可以达到 3 GB(适用于 windows 2003)。
但在您的情况下,我认为您正在分配连续的内存,因此发生了异常。
【讨论】:
+1 - 从未考虑过分配连续数组只能为您提供最大量的“连续”内存空闲的事实!真的很好。 /3GB 开关有点危险。许多驱动程序没有经过交换机测试,因此当操作系统限制为 1GB 时,它们可能会变得不稳定。不过没关系,因为他在 64 位 Windows 上运行。 为了帮助我理解您所说的内容,我将尝试改写它。你是说如果我以更小的块分配内存,我将能够获得更多的总内存分配? @Michael:我就是这么说的! ;) @Bill:可能,是的。如果您分配较小的块,它们不必是连续的,这意味着操作系统更有可能找到空间。举个简单的例子,假设您的应用程序恰好在地址空间的中间加载。然后,您所做的任何分配都不能大于地址空间的一半 - 它实际上被减半。您可以进行两次小分配,但不能进行一次大分配。现在想象一下,你在整个地方都放置了十几个块,而你的地址空间被切得更小了。【参考方案3】:您可以根据页面文件的允许分配尽可能多的内存 - 即使没有 /3GB 开关,您也可以毫不费力地分配 4GB 内存。
阅读this article 可以很好地了解如何考虑物理内存、虚拟内存和地址空间(这三者是不同的东西)。简而言之,您拥有的物理内存与 RAM 一样多,但您的应用程序实际上根本没有与物理内存交互——它只是一个方便的地方来存储虚拟内存中的数据。您的虚拟内存受页面文件大小的限制,您的应用程序可以使用的数量受其他应用程序使用量的限制(尽管您可以分配更多,前提是您实际上不使用它)。您在 32 位世界中的地址空间是 4GB。其中,2 GB 分配给内核(如果使用 /3BG 开关,则为 1 GB)。在剩下的 2GB 中,有些会被你的堆栈用完,有些会被你当前运行的程序用完,(以及所有的 dll 等等)。它会变得支离破碎,你只能获得这么多连续的空间——这就是你的分配失败的地方。但是由于该地址空间只是访问您为您分配的虚拟内存的一种便捷方式,因此可以分配更多的内存,并一次将其大块带入您的地址空间。
Raymond Chen has an example 了解如何分配 4GB 内存并将其中的一部分映射到地址空间的一部分。
在 32 位 Windows 下,可分配的最大容量为 16TB,而在 64 位 Windows 下则为 256TB。
如果您真正了解 Windows 中内存管理的工作原理,请阅读this article。
【讨论】:
【参考方案4】:在 ElephantsDream 项目期间,带有 Blender 3D 的 Blender Foundation 也遇到了类似的问题(尽管在 Mac 上)。不能包含链接,但谷歌:blender3d 内存分配问题,它将是第一项。
解决方案涉及文件映射。自己没有尝试过,但您可以在这里阅读:http://msdn.microsoft.com/en-us/library/aa366556(VS.85).aspx
【讨论】:
【参考方案5】:当 nBandBytes 为 466,560,000 时,您尝试分配 1.4 GB。 32 位应用程序通常只能访问 2 GB 内存(如果您使用 /3GB 启动并且可执行文件被标记为大地址空间感知,则更多)。你可能很难为你的大块内存找到这么多块连续的地址空间。
如果您想在 64 位操作系统上分配千兆字节的内存,请使用 64 位进程。
【讨论】:
你如何获得 1.4GB?您是否假设一个字节为 4 个字节? :p 3 * 466,560,000 。他正在分配 3 个数组。 正确,我希望能够分配 1.3 GB 的内存,但当我无法分配时,我感到很惊讶。 RE:“大地址空间感知”代码位于 .dll 中,并且 .dll 是使用 lare 地址空间感知设置编译的。是否需要以这种方式标记整个应用程序,还是仅标记 .dll 就可以? @Bill - exe 需要能够识别大地址空间【参考方案6】:您应该能够为每个进程分配总共大约 2GB 的空间。 This article (PDF) 解释了细节。但是,您可能无法获得甚至接近那么大的单个连续块。
【讨论】:
【参考方案7】:即使您以较小的块进行分配,也无法获得所需的内存,尤其是当周围的程序具有不可预测的内存行为时,或者如果您需要在不同的操作系统上运行。根据我的经验,32 位进程的堆空间上限约为 1.2GB。
在这个内存量下,我建议手动写入磁盘。将数组包装在一个管理内存并在必要时写入临时文件的类中。希望您的程序具有这样的特性,即您可以有效地缓存部分数据而不会过多地占用磁盘。
【讨论】:
【参考方案8】:Sysinternals VMMap 非常适合研究虚拟地址空间碎片,这可能会限制您可以分配多少连续内存。我建议将其设置为显示空闲空间,然后按大小排序以找到最大的空闲区域,然后按地址排序以查看最大的空闲区域(可能是重新定位的 DLL、共享内存区域或其他堆)之间的分隔。
正如其他人所建议的那样,避免极大的连续分配可能是最好的。
设置LARGE_ADDRESS_AWARE=YES
(如jalf 建议的那样)很好,只要您的应用程序所依赖的库与其兼容。如果这样做,您应该使用 AllocationPreference
注册表项集来测试您的代码,以启用自上而下的虚拟地址分配。
【讨论】:
好主意——我会试试VMMap。以上是关于您应该能够分配多少内存?的主要内容,如果未能解决你的问题,请参考以下文章