8

背景:我正在编写一个处理大量地理数据的 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 位应用程序下,您应该能够分配多少内存?

4

8 回答 8

11

操作系统想给你多少。默认情况下,Windows 允许 32 位进程拥有 2GB 的地址空间。这被分成几个块。为堆栈留出一个区域,为每个加载的可执行文件和 dll 留出其他区域。剩下的任何东西都可以动态分配,但不能保证它会是一个连续的大块。它可能是几个较小的块,每个几百 MB。

如果您使用 LargeAddressAware 标志进行编译,64 位 Windows 将允许您使用完整的 4GB 地址空间,这应该会有所帮助,但总的来说,

  • 你不应该假设可用内存是连续的。您应该能够使用多个较小的分配而不是几个大的分配,并且
  • 如果需要大量内存,则应将其编译为 64 位应用程序。
于 2009-06-23T17:48:25.267 回答
6

在 windows 32 位上,正常进程最多可以占用 2 GB,但使用/3GB开关它可以达到 3 GB(对于 windows 2003)。

但是在您的情况下,我认为您正在分配连续的内存,因此发生了异常。

于 2009-06-23T17:45:09.497 回答
4

您可以分配页面文件允许的尽可能多的内存——即使没有 /3GB 开关,您也可以毫不费力地分配 4GB 内存。

阅读这篇文章了解如何考虑物理内存、虚拟内存和地址空间(这三者都是不同的东西)。简而言之,您拥有的物理内存与 RAM 一样多,但您的应用程序实际上根本没有与物理内存进行交互——它只是一个方便的地方来存储虚拟内存中的数据。您的虚拟内存受到页面文件大小的限制,您的应用程序可以使用的数量受到其他应用程序使用量的限制(尽管您可以分配更多,前提是您实际上不使用它)。您在 32 位世界中的地址空间是 4GB。其中,2 GB 分配给内核(如果使用 /3BG 开关,则为 1 GB)。在剩下的 2GB 中,一些将被您的堆栈用完,一些将被您当前正在运行的程序(以及所有 dll 等)用完。它' s 会变得支离破碎,你只能获得这么多连续的空间——这就是你的分配失败的地方。但是由于该地址空间只是访问您为您分配的虚拟内存的一种便捷方式,因此可以分配更多的内存,并一次将一些块带入您的地址空间。

Raymond Chen 有一个如何分配 4GB 内存并将其中的一部分映射到地址空间的一部分的示例。

在 32 位 Windows 下,最大可分配 16TB 和 64 位 Windows 中的 256TB。

如果您真正了解 Windows 中内存管理的工作原理,请阅读这篇文章

于 2009-06-23T18:05:06.253 回答
2

在 ElephantsDream 项目中,Blender Foundation with Blender 3D 也有类似的问题(尽管在 Mac 上)。不能包括链接,但谷歌:blender3d 内存分配问题,它将是第一项。

该解决方案涉及文件映射。自己没有尝试过,但您可以在这里阅读:http: //msdn.microsoft.com/en-us/library/aa366556 (VS.85).aspx

于 2009-06-23T18:07:07.590 回答
1

当 nBandBytes 为 466,560,000 时,您尝试分配 1.4 GB。32 位应用程序通常只能访问 2 GB 内存(如果您使用 /3GB 启动并且可执行文件被标记为大地址空间感知,则更多)。你可能很难为你的大块内存找到这么多块连续的地址空间。

如果要在 64 位操作系统上分配千兆字节的内存,请使用 64 位进程。

于 2009-06-23T17:49:24.920 回答
1

您应该能够为每个进程分配总共大约 2GB 的空间。这篇文章(PDF) 解释了细节。但是,您可能无法获得甚至接近那么大的单个连续块。

于 2009-06-23T17:51:37.257 回答
1

即使您以较小的块分配,您也无法获得所需的内存,特别是如果周围的程序具有不可预测的内存行为,或者如果您需要在不同的操作系统上运行。根据我的经验,32 位进程的堆空间上限约为 1.2GB。

在这种内存量下,我建议手动写入磁盘。将数组包装在一个管理内存并在必要时写入临时文件的类中。希望您的程序具有这样的特性,即您可以有效地缓存部分数据而不会过多地占用磁盘。

于 2009-06-23T18:03:00.467 回答
1

Sysinternals VMMap非常适合研究虚拟地址空间碎片,这可能会限制您可以分配多少连续内存。我建议将其设置为显示空闲空间,然后按大小排序以找到最大的空闲区域,然后按地址排序以查看最大的空闲区域(可能是重新定位的 DLL、共享内存区域或其他堆)之间的分隔。

正如其他人所建议的那样,避免非常大的连续分配可能是最好的。

设置LARGE_ADDRESS_AWARE=YES(如 jalf 建议的那样)很好,只要您的应用程序所依赖的库与其兼容。如果这样做,您应该使用AllocationPreference设置为启用自上而下的虚拟地址分配的注册表项来测试您的代码。

于 2009-06-23T18:23:21.293 回答