windows 64位大malloc

bybem2ql  于 2022-12-05  发布在  Windows
关注(0)|答案(9)|浏览(204)

malloc()失败的原因是什么,特别是在64位中?
我的具体问题是尝试在64位系统上分配一个巨大的10GB内存块。机器有12GB的内存和32GB的交换空间。是的,malloc是极端的,但为什么会有问题呢?这是在Windows XP64中,同时使用Intel和MSFT编译器。malloc有时成功,有时不成功,大约50%。8GB的malloc总是有效的。20GB的malloc总是失败。如果malloc失败,重复的请求将无法工作,除非我退出进程并重新启动一个新的进程(这样就有50%的机会成功)。没有其他大的应用程序在运行。即使是在重新启动后立即发生。
我可以想象,如果您已经用完了可用的32位(或31位)地址空间,从而没有足够大的地址范围来分配给您的请求,则malloc在32位中失败。
我也可以想象,如果你用完了你的物理内存和硬盘交换空间,malloc会失败。
但是为什么malloc会失败呢?我想不出其他原因。
我对一般的malloc问题更感兴趣,而不是我的具体例子,我可能会用内存Map文件来替换它。失败的malloc()比其他任何东西都更像是一个谜题......希望理解你的工具,而不是对基本原理感到惊讶。

eit6fx6z

eit6fx6z1#

malloc试图分配一个连续的内存范围,而这最初将在真实的内存中,这仅仅是由于交换内存的工作方式(至少我记得是这样)。很容易的是,您的操作系统有时无法找到一个连续的10 GB内存块,但仍然在同一时间将所有需要实际内存的进程留在RAM中(在这一点上,您的malloc将失败)。
你真的需要10 GB的连续内存吗?或者你能用一个存储类/结构体包裹几个较小的块,并以块的形式使用内存吗?这就放宽了巨大的连续内存的要求,也允许你的程序使用交换文件来存储较少使用的块。

laik7k3q

laik7k3q2#

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

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

如果虚拟地址空间保留失败(即使从您所说的判断,它不应该失败),Sysinternals VMMap可能有助于解释原因。打开“显示可用区域”以查看可用虚拟地址空间是如何碎片化的。

1cosmwyk

1cosmwyk3#

这里有一个官方来源,说明堆的最大请求大小是由链接的CRT库定义的(除了前面的代码有整数溢出到0,这就是为什么你没有得到NULL)(_HEAP_MAXREQ)。
http://msdn.microsoft.com/en-us/library/6ewkz86d.aspx
看看我的答案在这里为大窗口分配,我包括一个参考MS文件的Vista/2008内存模型的进步。
简而言之,普通CRT不支持任何大于4GB的堆大小,即使是对于本机64位进程也是如此。您 * 必须 * 使用VirtualAlloc* 或CreateFileMapping或其他类似方法。
哦,我还注意到你声称你的更大的分配实际上是成功的,这实际上是不正确的,你误解了malloc(0x 200000000);(十六进制为8 gb),发生的情况是由于强制转换或测试工具的其他影响,您请求分配0字节,您肯定不会看到任何大于0xfffff 000字节堆的东西被提交,您只是看到整数溢出向下强制转换。

言归正传或 * 保存健康的小贴士 *

使用MALLOC(或任何其他动态请求)分配内存的*唯一方法

void *foo = malloc(SIZE);

动态内存请求的值决不能(我无法强调这一点)在请求的“()”括号内计算

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

危险在于会发生整数溢出。
在调用时执行算术运算总是编码错误,您必须始终在评估请求的语句之前计算要请求的数据的总计
为什么会这么糟糕呢?我们知道这是一个错误,因为在一个点上,一个 * 请求 * 动态资源,必须在未来的一个点上,我们将 * 使用 * 这个资源。
要使用我们所请求的内容,我们必须知道它有多大?(例如,数组计数、类型大小等)。
这意味着,如果我们在资源请求的()中看到任何算术运算,这就是一个错误,因为我们必须再次 * 复制该代码,* 才能正确使用该数据。

ioekq8ef

ioekq8ef4#

您是否尝试过使用heap functions来分配内存?

5vf7fwbs

5vf7fwbs5#

这里只是一个猜测,但是malloc分配了连续的内存,而您的堆上可能没有足够大的连续部分。
如果20GB malloc失败,四个5GB malloc是否成功?如果成功,则是连续空间问题。
您是否检查过编译器开关中是否存在限制总堆大小或最大堆块大小的内容?
你有没有试过写一个程序声明一个所需大小的静态变量?如果这样做有效的话,你可以在那个空间里实现你自己的堆。

kmynzznz

kmynzznz6#

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

nfzehxib

nfzehxib7#

但是为什么malloc会失败呢?我想不出其他原因
正如前面多次暗示的那样,由于内存碎片

lh80um4z

lh80um4z8#

最有可能的是碎片化。为了简单起见,让我们举一个例子。
内存由一个12 kb的模块组成。这个内存在MMU中被组织成1 kb的块。所以,你有12 x 1 kb的块。你的操作系统使用100字节,但这基本上是管理页表的代码。所以,你不能把它换掉。那么,你的应用程序每个都使用100字节。
现在,如果只运行操作系统和应用程序(200字节),您将已经使用200字节的内存(占用2kb的块),正好为malloc()留下10 kb可用空间。
现在,您通过malloc()启动了两个缓冲区- A(900字节)、B(200字节)。然后,您释放了A。现在,您有9.8kb的可用空间(非连续)。因此,您尝试malloc() C(9 kb)。突然,您失败了。
您在尾端有8.9k的连续块,在前端有0.9k的连续块。您不能将第一个块重新Map到尾端,因为B会延伸到第一个1 k块和第二个1 k块。
您仍然可以malloc()单个8 kb块。
当然,这个例子有点做作,但希望它能有所帮助。

ibrsph3r

ibrsph3r9#

我发现这个问题很有趣,所以我试着从理论Angular 研究它:
在64位(实际上由于芯片限制只有48位可用,由于操作系统的限制只有更少(44位?))中,你当然不应该受到虚拟内存碎片的限制,即缺乏连续的虚拟地址空间。原因是有太多的虚拟地址空间,耗尽它是不切实际的。
此外,我们可以预期,* 物理 * 内存碎片应该不是一个问题,因为虚拟内存意味着不需要连续的物理内存地址范围来满足分配请求。相反,它可以满足任何足够大的内存页面集。
所以你一定是遇到了 * 别的事情 *:即应用于虚拟存储器的一些其它限制。
另一个在Windows上明确存在的限制是提交限制。有关此限制的更多信息:
https://web.archive.org/web/20150109180451/http://blogs.technet.com/b/markrussinovich/archive/2008/11/17/3155406.aspx
可能存在其他可能的限制,例如,实际实现必须如何与实际硬件一起工作的怪癖。想象一下,当试图创建一个虚拟地址空间到物理地址空间的Map时,你用完了页表中的条目来进行虚拟地址Map...操作系统内存分配器代码关心处理这种不太可能的情况吗?也许不关心...
您可以在此处阅读有关页表实际如何进行虚拟地址转换的更多信息:
http://en.wikipedia.org/wiki/Memory_management_unit

相关问题