Linux乐观malloc:内存不足时new总是会抛出吗?

Posted

技术标签:

【中文标题】Linux乐观malloc:内存不足时new总是会抛出吗?【英文标题】:Linux optimistic malloc: will new always throw when out of memory? 【发布时间】:2010-12-11 22:57:03 【问题描述】:

我一直在阅读有关 Linux 内存不足的情况,手册页中的以下段落让我思考:

默认情况下,Linux 遵循乐观的内存分配策略。这意味着当 malloc() 返回非 NULL 时,不能保证内存确实可用。这是一个非常糟糕的错误。如果发现系统内存不足,一个或多个进程将被臭名昭著的 OOM 杀手杀死。 [...]

考虑到 operator new 实现最终会在某个时候调用 malloc,是否有任何保证 new 会在 Linux 上实际抛出?如果没有,如何处理这种明显无法检测到的错误情况?

【问题讨论】:

现在看,你在想。别。因为rpg,你无法控制投掷什么,谁得到指针,谁被OOM杀死或为什么。这不取决于你。这只是 Linux。 @Jonathan Leffler:我把标题改回“always;”如果 new 成功返回但没有可用内存,则它不符合 C++ 标准;因此,问题真的是“新的总是会抛出吗?”或者,“new 永远不会扔吗?”而不是“新的会扔吗?” (不过,我不想踩到任何脚趾……如果有人认为我错了,请告诉我……) 感谢大家的回答! @James:我对你的改写没有意见。我确实认为原标题“Linux 乐观 malloc:new 总是会抛出?”错了;如果 new 总是抛出(即使没有内存不足),没有人会用 C++ 编写代码。 【参考方案1】:

视情况而定; you can configure the kernel's overcommit settings 使用 vm.overcommit_memory。

Herb Sutter 几年前讨论过this behavior is actually nonconforming to the C++ standard:

“在某些操作系统上,特别是Linux,内存分配总是成功的。句号。分配怎么能总是成功,即使请求的内存真的不可用?原因是分配本身只是记录一个请求内存;在幕后,(物理或虚拟)内存实际上并没有真正提交给请求进程,具有真正的后备存储,直到内存被实际使用。

"请注意,如果 new 直接使用操作系统的功能,那么 new 将始终成功,但任何后续的无辜代码(如 buf[100] = 'c'; )都可能抛出、失败或停止。从标准 C++ 的角度来看, 这两种效果都是不一致的,因为 C++ 标准要求如果 new 不能提交足够的内存,它必须失败(这不会),并且像 buf[100] = 'c' 这样的代码不应该抛出异常或其他失败(这可能)。”

【讨论】:

buf[100] = 'c' 不会失败或抛出异常。应用程序可能会在那个时候被系统杀死,但应用程序可能会在任何时间被系统杀死。 该标准允许符合标准的实现在超出翻译限制时执行任何操作,前提是对于“翻译限制”部分中给出的每个限制,它至少可以正确处理至少一个程序名义上行使该限制。我认为定义一个可以提供更强保证的 C 实现类别会很有帮助,例如定义一组环境要求和定义的故障模式,并保证... ...如果所有环境要求都得到满足,并且它被赋予了一个严格符合的程序[或任何符合我称之为“选择性符合”的更宽松标准的程序,它将要么在标准中处理程序-定义的方式或以它记录的方式之一失败。这将使得有可能知道,如果一个选择性符合的实现被提供给一个[无论术语]符合的实现,它似乎无法正常工作,除非它确实这样做了,并且可以不会以所列以外的任何方式失败。【参考方案2】:

我认为 malloc 仍然可以返回 NULL。原因是可用系统内存(RAM + 交换)和进程地址空间中的数量之间存在差异。

例如,如果您在标准 x86 linux 上向 malloc 请求 3GB 内存,它肯定会返回 NULL,因为考虑到分配给用户空间应用程序的内存量,这是不可能的。

【讨论】:

我是这么想的,但是我在网上看到一些报告说somone分配了1TB的内存,不幸的是它成功了。 我会说那显然是一台 64 位机器。即使他没有那么多可用的mem,进程的地址空间也大于1TB。【参考方案3】:

你无法在你的软件中处理它,纯粹而简单。

对于您的应用程序,您将收到一个完全有效的指针。一旦你尝试访问它,它会在内核中产生一个页面错误,内核会尝试为它分配一个物理页面,如果它不能......繁荣。

但正如您所见,所有这些都发生在内核内部,您的应用程序无法看到。如果它是一个关键系统,您可以在系统上一起禁用过度使用。

【讨论】:

【参考方案4】:

是的,有一个保证 new 最终会抛出。无论是否过度使用,地址空间的数量都是有限的。所以如果你继续分配内存,迟早你会用完地址空间,new 会被强制抛出。

【讨论】:

你能备份一下吗?即使使用 PAE,在 32 位上也应该很容易。 @Jed - 您是在寻找更深入的推理还是想要一个示例来证明这一点?如果是后者,只需在无限循环中继续调用 operator new 即可。使用 gcc 3.4.5 和 gcc 4.4.0 中的 libstdc++ 并分配 1 meg 缓冲区,在 3,055 次调用后引发了 bad_alloc 异常。【参考方案5】:

如果我错了,请原谅我,但是尝试将分配的内存归零是否足以保证您拥有所请求的每个字节?或者甚至只是写入最后一个字节,如果内存不是你的,它会抛出异常吗?

如果是这样,您可以尝试写入内存的最后一个(也是第一个?)字节,看看它是否工作正常,如果不能,您可以从 malloc 返回 null。

【讨论】:

您至少需要触摸每一页。只有第一页和最后一页是不够的,因为它只会逐页真正分配(至少在大多数正常的机器上) Steven 是正确的,你必须触摸每一页。但是,这种方法的问题通常在于,如果在此过程中后备存储的提交失败,您将无能为力,因为您的应用程序将因分段错误而崩溃,因为您将尝试写入一些您没有写入的内存' t 实际上拥有。您无法从分段错误中恢复(有时您可以处理sigsegv 信号,但处理后您肯定无法继续执行)。 它不会因分段错误而失败。 OOM 杀手的目标死于SIGKILL - 你不应该认为这与被超级用户手动杀死有什么不同。 我想你可以启动一个子进程,创建共享内存,然后把它交给父进程,然后退出……如果子进程崩溃,你就完了! :p

以上是关于Linux乐观malloc:内存不足时new总是会抛出吗?的主要内容,如果未能解决你的问题,请参考以下文章

细说new与malloc的10点区别

细说new与malloc的10点区别

malloc 和 new 的区别

linux oom-killer

使用CoTaskMemAlloc时,我应该总是调用CoTaskMemFree吗?

new/malloc