你能在C或C++中分配一个非常大的单一内存块(> 4GB)吗?

43

现在有很大的内存,我想知道是否可能分配一个大于4GB的单一内存块?还是需要分配多个较小的内存块并处理它们之间的切换?

为什么呢? 我正在处理一些openstreetmap xml数据,这些文件非常大。由于无法一次性加载所有数据,所以我目前正在使用流式传输方式,但我对malloc或new的上限产生了好奇。


有人可能会想知道为什么需要这样做。 :) - epochwolf
8
考虑到这一需求并不太困难。 - shoosh
物理内存仿真是首先想到的事情... - Serafina Brocious
1
这就是为什么XML不应用作数据存储,特别是不应作为数据库。它最初设计作为一种数据传输机制。 - Roger Nelson
听起来像是数据传输。但同意,如果你的意思是这样的话,XML可能被过度使用了。 - ApplePieIsGood
10个回答

27

简短回答:不太可能。

为了实现这个目标,你绝对需要使用64位处理器。此外,还要考虑操作系统是否支持将超过4G的内存分配给单个进程。

理论上来说,这是可能的,但你必须阅读内存分配器的文档。此外,你还会更容易受到内存碎片问题的影响。

关于Windows内存管理有很好的信息。


6
实际上,32位英特尔处理器拥有36位地址以支持64 GB的内存 - 只是桌面版Windows的许可证限制了你只能使用4GB。但Linux/BSD可以在32位CPU上访问64GB的内存。 - Martin Beckett
5
据我所知,即使如此,您仍然会受到3GB进程限制的限制。 - Marco van de Voort
4
不,如果您有一个64位程序,就没有3GB的进程限制。它可以使用机器支持的虚拟地址空间(大约48-52位,我忘记了确切的数字)和操作系统允许进程使用的物理内存。 - R.. GitHub STOP HELPING ICE

24

物理和虚拟内存布局概述

你需要一个64位的CPU和操作系统构建,几乎肯定需要足够的内存来避免工作集抖动。一些背景知识:

32位机器(大多数情况下)具有可以存储2^32(4,294,967,296)个唯一值中的一个的寄存器。这意味着32位指针可以寻址2^32个唯一的内存位置,这就是魔法的4GB限制的来源。

一些32位系统(例如SPARCV8或Xeon)具有MMU(内存管理单元),可以通过一种技巧允许使用更多物理内存。这允许多个进程占用总共超过4GB的内存,但每个进程都限于其自己的32位虚拟地址空间。对于查看虚拟地址空间的单个进程,只能使用32位指针映射2^32个不同的物理位置。

我不会详细解释,但本演示文稿(警告:powerpoint)描述了这是如何工作的。一些操作系统具有设施(例如上面提到的此处 - 感谢FP),可以在用户级别控制下操作MMU并将不同的物理位置换入虚拟地址空间。

操作系统和内存映射I/O将占用一些虚拟地址空间,因此并非所有4GB都可供进程使用。例如,Windows默认占用2GB,但可以在引导时调用/3G开关将其设置为仅占用1GB。这意味着,在这种32位架构上,单个进程只能在内存中构建少于4GB的连续数据结构。
这意味着您必须明确使用Windows上的PAE设施或Linux上的等效设施手动交换覆盖。这不一定很难,但需要一些时间来使其正常工作。
另外,您可以获得具有大量内存的64位计算机,这些问题基本上就不存在了。具有64位指针的64位架构理论上可以构建具有多达2^64(18,446,744,073,709,551,616)个唯一地址的连续数据结构,这允许构建和管理更大的连续数据结构。

2
或者你可以选择简单的AMD Phenom或英特尔四核处理器。我最近组装了一台四核机器,配备8GB内存和4个500GB硬盘,仅花费1000加元左右。只要不考虑显卡,就能以较低的价格获得很强大的性能。 - Kibbee

22

内存映射文件的优点在于您可以打开比4Gb大得多的文件(在NTFS上几乎无限制!),并且在其中有多个小于4Gb的内存窗口。
它比将文件打开并读入内存更加高效,在大多数操作系统上,它使用内置的分页支持。


"almost much more efficient" 的意思是什么? - andy
抱歉,我改变了句子并多留了一个词。 - Martin Beckett
1
内存映射文件避免了整个文件复制到提交的内存(可能大部分都返回到分页文件)的问题,因为它只是将原始文件用作已提交内存的支持存储器。 - QBziZ
2
NTFS 上的最大文件大小为 2^64,虽然不是无限的,但足够大了,可以四处喝酒庆祝! - Martin Beckett
NTFS中的最大文件大小远小于实际的mgb。理论上:16 EiB减1 KiB(264-210字节)。实现上:16 TiB减64 KiB(244-216字节)。http://en.wikipedia.org/wiki/NTFS。 - 1800 INFORMATION
但是对于工程师来说,16 TiB已经足够接近无限了。我的意思是,“谁会需要超过16TiB的文件呢?” - xDaizu

14

如果使用64位操作系统(以及内存足够的机器),这将不会是一个问题。

如果malloc无法处理,则操作系统肯定会提供API,允许您直接分配内存。在Windows下,您可以使用VirtualAlloc API。


12

这取决于您使用的C编译器和平台(当然),但没有根本性的原因,为什么您不能分配最大的连续可用内存块,这可能少于您所需的内存大小。当然,您可能必须使用64位系统才能寻址那么多RAM...

有关历史和详细信息,请参见Malloc

在alloc.h中调用HeapMax以获取最大可用块大小。


9

您是否考虑过使用内存映射文件?由于您正在加载非常大的文件,因此这似乎是最好的选择。


6
这取决于操作系统是否提供虚拟地址空间,允许寻址超过4GB的内存,并且编译器是否支持使用new/malloc进行分配。对于32位Windows,由于指针大小为32位,因此将虚拟地址空间限制为4GB,因此您将无法获得单个大于4GB的块。(您可以使用物理地址扩展来获取超过4GB的内存;但是,我认为您必须将该内存映射到自己的4GB虚拟地址空间中)对于64位Windows,VC++编译器支持64位指针,理论上将虚拟地址空间的限制提高到8TB。我怀疑Linux/gcc也是如此-32位不允许,而64位允许。

只是好奇,你从哪里得到了8TB的信息?英特尔文档提供了48位实际地址大小,这给出了256TB的地址空间。 - Branan
我应该没有用“理论上”的措辞。实际数字来自于Windows版本的内存限制 - http://msdn.microsoft.com/en-us/library/aa366778(VS.85).aspx - Franci Penov

4
如Rob所指出的,在Windows上使用VirtualAlloc和匿名文件映射是一个不错的选择。但是,针对您的问题,“C或C ++”是否可以进行分配,答案是否,即使在Win7 RC 64上也不支持
在exe文件的PE / COFF规范中,指定HEAP保留和HEAP提交的字段是32位数量。这与当前堆实现在Windows CRT中的物理大小限制相一致,该大小接近4GB。因此,无法从C / C ++中分配超过4GB(技术上,OS支持CreateFileMapping和VirtualAlloc / VirtualAllocNuma等设施不属于C或C ++)。
另外,请注意,存在称为页表的潜在x86或amd64 ABI结构。 这将实际上完成您所关注的操作,即为您的较大请求分配较小的块,即使这是在内核内存中发生的,整个系统仍会产生影响,因为这些表是有限的。
如果您要分配如此大量的内存,最好根据分配粒度(VirtualAlloc强制执行)进行分配,并确定启用更大页面的可选标志或方法。 4KB页面是386的初始页面大小,随后Pentium增加了4MB。 今天, AMD64(AMD Family 10h处理器的软件优化指南)具有最大页面表条目大小为1GB。 这意味着对于您的情况,假设您只使用了4GB,则仅需要在内核目录中查找\分配和权限您的进程的内存的4个唯一条目。 Microsoft还发布了这个手册,阐明了Vista / 2008平台及更高版本的应用程序内存和其使用的一些细节。

3

如果您的系统上的size_t大于32位,那么您已经成功地跨过了第一道障碍。但是,C和C++标准并不负责确定任何特定的new或malloc调用是否成功(除了malloc大小为0的情况)。这完全取决于操作系统和堆的当前状态。


2
像其他人所说的,使用64位机器是最好的选择。但是即使在32位英特尔机器上,如果您的操作系统和CPU支持PAE,则可以访问超过4GB的内存区域。不幸的是,32位WinXP不支持此功能(32位Vista呢?)。Linux默认允许您这样做,但是由于指针仍然是32位,即使使用mmap(),您也将受到限制。
您应该让操作系统为您处理内存管理。进入可以处理如此巨大RAM的环境中,然后将XML文件读入数据结构并让其为您分配空间。然后在内存中对数据结构进行操作,而不是直接操作XML文件本身。
即使在64位系统中,您也无法控制程序的哪些部分实际上存储在RAM中、缓存中或分页到磁盘中,至少在大多数情况下是这样的,因为操作系统和MMU会自己处理这些问题。

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