64位大型mallocs

时间:2009-05-07 06:58:43

标签: windows memory 64-bit malloc virtual-memory

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

我的具体问题是试图在64位系统上使用巨大的10GB RAM。 该机器具有12GB的RAM和32 GB的交换空间。 是的,malloc极端,但为什么会出现问题呢?这是在Windows XP64中同时包含Intel和MSFT编译器。 malloc有时成功,有时不成功,约50%。 8GB malloc总是工作,20GB malloc总是失败。如果malloc失败,重复的请求将无法工作,除非我退出该过程并再次启动一个新进程(这将成功获得50%的成功)。没有其他大型应用程序正在运行它会在重新启动后立即发生。

我可以想象如果你用掉了32位(或31位)可用的地址空间,那么malloc就会失败32位,这样就没有足够大的地址范围可以分配给你的请求了。

如果您用完了物理内存您的硬盘交换空间,我还可以想象malloc会失败。对我来说情况并非如此。

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

我对一般malloc问题比我的具体例子更感兴趣,无论如何我都可能用内存映射文件替换它。失败的malloc()只是一个难题而不是其他任何东西......想要了解你的工具而不是对基础知识感到惊讶。

9 个答案:

答案 0 :(得分:8)

malloc尝试分配一个连续的内存范围,这最初只是在实内存中,因为交换内存的工作原理(至少就我记忆所知)。可能很容易就是你的操作系统有时无法找到连续的10GB内存块,并且仍然会在RAM中同时保留所有需要实内存的进程(此时你的malloc将会失败)。

你真的需要10GB的连续内存,或者你能够将存储类/结构包装在几个较小的块中并以块的形式使用你的内存吗?这放宽了巨大的连续需求,并且还应允许程序将交换文件用于较少使用的块。

答案 1 :(得分:5)

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

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

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

答案 2 :(得分:3)

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

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

您是否检查过编译器开关是否存在限制总堆大小或最大堆块大小的任何内容?

您是否尝试过编写声明所需大小的静态变量的程序?如果这样可行,您可以在该空间中使用大型malloc实现自己的堆。

答案 3 :(得分:2)

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

答案 4 :(得分:1)

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

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

查看我的答案here以获取大型Windows分配,我在Vista / 2008内存模型改进中包含了对MS论文的引用。

简而言之,股票CRT不支持,即使对于本机64位进程,任何堆大小超过4gb。您使用VirtualAlloc *或CreateFileMapping或其他类似物。

哦,我也注意到你声称你的更大的分配实际上是成功的,这实际上是不正确的,你错误地解释了malloc(0x200000000); (这是十六进制的8gb),正在发生的是你要求一个0字节的分配,因为你的测试工具的强制转换或其他一些影响,你绝对不会观察任何大于提交的0xfffff000字节堆的东西,它是只是你看到整数溢出下来。

WORD TO THE WYSE 或* TIPS拯救你的整洁*

用MALLOC(或任何其他动态请求)分配记忆的方式

void *foo = malloc(SIZE);

动态内存请求的值绝对不得(我不能强调)在“()”请求的“()”中计算

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

危险在于会发生整数溢出。

在调用时执行算术始终是编码 ERROR 必须始终计算 TOTAL SUM 数据到在评估请求的声明之前请求。

为什么这么糟糕?我们知道这是一个错误,因为请求是针对动态资源的,必须是未来我们使用<的一个点/ em>这个资源。

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

这意味着,如果我们在资源请求的()内部看到任何算术,那么这是一个错误,因为我们必须再次复制该代码 为了适当地使用这些数据。

答案 5 :(得分:1)

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

答案 6 :(得分:0)

  

但是为什么malloc会失败呢?一世   想不出其他原因

如前几次隐含陈述,因为内存碎片

答案 7 :(得分:0)

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

在64位(由于芯片限制实际上48位可用,并且由于操作系统限制,更少(44位?))你当然应该受到虚拟内存碎片的限制,即缺少连续的虚拟地址空间。原因是只有这么多的虚拟地址空间使用它是非常不切实际的。

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

所以你必须遇到别的东西:o.e。适用于虚拟内存的其他一些限制。

Windows上肯定存在的另一个限制是提交限制。有关此内容的更多信息:

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

可能存在其他可能的限制,例如关于实际实现如何与实际硬件一起工作的怪癖。想象一下,当尝试创建虚拟地址空间到物理地址空间的映射时,用完页表中的条目来进行虚拟地址映射... OS内存分配器代码是否关心处理这种不太可能的情况?也许不是......

您可以在此处阅读有关页表实际工作如何进行虚拟地址转换的更多信息:

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

答案 8 :(得分:-1)

很可能是碎片化。为简单起见,我们举个例子。

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

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

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

你的尾端有8.9k连续,前端有0.9k。您无法将第一个块重新映射到结尾,因为B在第一个1k和第二个1k块上延伸。

您仍然可以malloc()一个8kb的阻止。

当然,这个例子有点做作,但希望它有所帮助。