18

malloc() 失败的原因是什么,尤其是在 64 位中?

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

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

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

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

我对一般 malloc 问题比我的具体示例更感兴趣,无论如何我可能会用内存映射文件替换它。失败的 malloc() 比其他任何事情都更像是一个谜……渴望了解您的工具而不是对基本原理感到惊讶。

4

9 回答 9

8

malloc 尝试分配一个连续的内存范围,这最初将在实际内存中,这仅仅是由于交换内存的工作方式(至少据我所知)。很可能您的操作系统有时无法找到一个连续的 10gb 内存块,并且仍然将所有需要实际内存的进程同时留在 RAM 中(此时您的 malloc 将失败)。

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

于 2009-05-07T07:18:19.103 回答
6

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

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

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

于 2009-05-07T17:39:13.387 回答
2

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

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

您是否检查过编译器开关是否限制了总堆大小或最大堆块大小?

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

于 2009-05-07T07:20:17.790 回答
2

您是否尝试过使用堆函数来分配内存?

于 2009-05-07T07:03:32.260 回答
2

这是一个官方消息来源,声明堆的最大请求大小由您的链接 CRT 库定义(除了您之前的代码整数溢出变为 0,这就是您没有返回 NULL 的原因)(_HEAP_MAXREQ)。

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

在此处查看我对大窗口分配的回答,我包括对有关 Vista/2008 内存模型改进的 MS 论文的参考。

简而言之,即使对于原生 64 位进程,普通 CRT 也不支持任何大于 4gb 的堆大小。您必须使用 VirtualAlloc* 或 CreateFileMapping 或其他一些类似物。

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

对 WYSE说的话或 * 保存您的头脑清醒的提示*

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

void *foo = malloc(SIZE);

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

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

危险是会发生整数溢出。

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

为什么这么糟糕?我们知道这是一个错误,因为在请求动态资源时,将来一定会有我们将使用该资源的时间点。

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

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

于 2009-06-02T09:04:36.260 回答
1

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

于 2013-04-24T04:54:28.877 回答
0

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

内存由一个 12kb 模块组成。该内存在 MMU 中被组织成 1kb 的块。所以,你有 12 x 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 块。

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

于 2009-05-07T14:01:38.307 回答
0

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

正如之前多次暗示的那样,由于内存碎片

于 2009-05-07T10:56:15.257 回答
0

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

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

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

因此,您必须遇到其他问题:或适用于虚拟内存的其他一些限制。

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

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

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

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

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

于 2015-03-20T23:49:25.347 回答