64位大型mallocs

malloc()会失败的原因是什么,特别是在64位?

我的具体问题是在64位系统上尝试malloc一个巨大的10GB块。 该机器有12GB的RAM和32GB的交换。 是的,malloc是极端的,但为什么会是一个问题呢? 这是在Windows XP64与英特尔和MSFT编译器。 malloc有时成功,有时不成功,约50%。 8GB malloc总是工作,20GB malloc总是失败。 如果一个malloc失败了,重复的请求将不会起作用,除非我退出这个进程并重新开始一个新的进程(然后成功的时候会有50%的成功)。 没有其他大应用程序正在运行。 即使在重新启动后也会立即发生。

我可以想象一个malloc在32位失败,如果你已经使用了32位(或31位)的可用地址空间,那么没有足够大的地址范围来分配你的请求。

我也可以想象,如果你用完了你的物理RAM 你的硬盘交换空间,malloc会失败。 这不是我的情况。

但是为什么malloc可能会失败呢? 我想不出其他的原因。

我对通用malloc问题比我的具体例子更感兴趣,我可能会用内存映射文件replace它。 失败的malloc()只不过是一个比其他任何东西都更让人困惑的东西……那种渴望理解你的工具而不会被基础知识所惊讶的东西。

malloc试图分配一个连续的内存范围,这将最初在真正的内存中,只是由于交换内存的工作原理(至少据我记忆)。 可能很容易,你的操作系统有时不能找到一块连续10GB的内存,并且同时把所有需要真实内存的进程留在内存中(这时你的malloc将会失败)。

你真的需要10GB的连续内存,或者你能够围绕几个较小的块包装一个存储类/结构,而不是使用你的内存块? 这放宽了巨大的连续需求,也应该允许你的程序使用较少使用的块的交换文件。

您是否尝试过直接使用VirtualAlloc()VirtualFree() 这可能有助于隔离问题。

  • 你会绕过C运行时堆和NT堆。
  • 您可以保留虚拟地址空间,然后提交它。 这将告诉你哪个操作失败。

如果虚拟地址空间保留失败(即使不应该,根据你所说的), Sysinternals VMMap可能有助于解释原因。 打开“显示免费区域”以查看免费虚拟地址空间是如何分段的。

在这里只是一个猜测,但malloc分配连续的内存,你可能没有足够大的连续的部分在你的堆。 这里有几件事我会尝试;

如果20GB malloc失败,那么4个5GB malloc会成功吗? 如果是这样,这是一个连续的空间问题。

你有没有检查你的编译器开关的任何限制总堆大小或最大的堆块大小?

你有没有试过编写一个声明所需大小的静态变量的程序? 如果这个工作,你可以在这个空间中实现自己的堆大malloc。

你有没有尝试过使用堆函数来分配你的内存呢?

这是一个正式的来源,说明堆的最大请求大小是由你的链接CRT库(除了你的前面的代码有整数溢出到0,这就是为什么你没有返回NULL)(_HEAP_MAXREQ)定义。

http://msdn.microsoft.com/en-us/library/6ewkz86d.aspx

看看我的答案这里的大窗口分配,我包括一个关于Vista / 2008内存模型进展的MS文件的参考。

总之,股票CRT不支持,即使是一个本地的64位进程任何堆大小超过4GB的。 必须使用VirtualAlloc *或CreateFileMapping或其他类似的东西。

哦,我也注意到你声称你的更大的分配实际上是成功的,这实际上是不正确的,你错误解释malloc(0x200000000); (这是十六进制的8GB),由于测试用具的强制转换或其他影响,您正在请求0字节分配,所以您绝对不会观察到任何大于0xfffff000字节堆的事情,这是只是你看到整型溢出铸造。

告诉WYSE或*提示保存您的HEAP SANITY *

只有通过 MALLOC(或任何其他动态请求)分配内存的方法

 void *foo = malloc(SIZE); 

动态内存请求的价值绝不能在(“)”请求的范围内计算(我无法承受)

 mytype *foo = (mytype *) malloc(sizeof(mytype) * 2); 

危险是会发生整数溢出。

在调用时执行算术总是一个编码错误 ,您必须总是计算在请求评估语句之前要请求的数据的总和。

为什么这么糟糕? 我们知道这是一个错误,因为请求动态资源的地方,未来我们将会使用这个资源。

要使用我们所要求的,我们必须知道它有多大? (例如数组数量,类型大小等)。

这意味着,如果我们在资源请求的()中看到任何算术,那么这是一个错误,因为我们必须重复该代码才能正确使用这些数据。

问题是,当您编译64位应用程序时,Visual Studio没有定义WIN64,它通常仍然保留WIN32,这对于64位应用程序是错误的。 这会导致运行时在定义_HEAP_MAXREQ时使用32位值,因此所有大malloc()都会失败。 如果您将项目(在项目属性,预处理定义下)更改为WIN64,那么非常大的malloc()应该没有问题。

但是为什么malloc可能会失败呢? 我想不出其他的原因

正如之前几次暗示的,由于内存碎片

我发现这个问题很有意思,所以我试图从理论上的POV研究它:

在64位(由于操作系统的限制,实际上是48位可用,由于操作系统的限制,实际上是48位),您当然应该受到虚拟内存碎片的限制,即缺少连续的虚拟地址空间。 原因是虚拟地址空间太多,所以耗尽它是不切实际的。

另外,我们可以预料物理内存碎片不应该成为问题,因为虚拟内存意味着不需要是连续的物理内存地址范围以满足分配请求。 相反,它可以满足任何足够大的一组内存页面。

所以你必须遇到其他的问题:适用于虚拟内存的其他限制。

在Windows上肯定存在的另一个限制是提交限制。 更多的信息在这:

http://blogs.technet.com/b/markrussinovich/archive/2008/11/17/3155406.aspx

其他可能的限制可能存在,例如,实际实现如何与实际硬件配合。 想象一下,当试图创建虚拟地址空间到物理地址空间的映射时,用尽了页表中的条目来执行虚拟地址映射……操作系统内存分配器代码是否在处理这种不太可能的情况? 也许不是…

你可以阅读更多关于页表如何实际工作来做虚拟地址转换的信息:

http://en.wikipedia.org/wiki/Memory_management_unit

这是最可能的分裂。 为了简单,让我们用一个例子。

内存由一个12kb的模块组成。 这个内存在MMU中被组织成1kb的块。 所以,你有12×1kb块。 您的操作系统使用100个字节,但这基本上是管理页表的代码。 所以,你不能把它换掉。 然后,你的应用程序全部使用100个字节。

现在,只有您的操作系统和您的应用程序运行(200字节),您将已经使用200字节的内存(占用2kb的块)。 正好10kb可用于malloc()

现在,你开始malloc()几个缓冲区 – A(900字节),B(200字节)。 然后,你释放A.现在,你有9.8kb免费(非连续)。 所以,你尝试malloc() C(9kb)。 突然,你失败了。

你有8.9k连续在尾端和0.9k在前端。 您不能将第一个块重新映射到最后,因为B展开了第一个1k和第二个1k块。

你仍然可以malloc()一个8kb块。

当然,这个例子是有点人为的,但希望它有帮助。