【问题标题】:Why does malloc() or new never return NULL? [duplicate]为什么 malloc() 或 new 从不返回 NULL? [复制]
【发布时间】:2013-05-16 11:28:15
【问题描述】:

正如我在here 中描述的那样,我正在编写一个需要大量内存用于缓存目的的应用程序。现在我正在玩一些 malloc / new 结构来弄清楚我如何实现它。我做了一个奇怪的观察:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  while(1) {
    char *foo = (char*)malloc(1024);// new char[1024];
    if(foo == NULL) { 
      printf("Couldn't alloc\n");
      fflush(stdout);
      return 0;
    }
  }
  return 0;
}

为什么永远无法到达 printf?如果我的系统内存不足,则据说 malloc 返回 NULL,正如 here 所解释的那样。但我总是收到 SIGKILL(我正在使用 linux...)。

【问题讨论】:

  • @slugonamission: Overcommit/OOM 杀手也是我的第一个想法,但 OP 取消引用指针,因此实际上没有理由崩溃(或 SIGKILL)。
  • @Damon 但是你确定堆实现没有试图访问内核返回的内存吗?
  • 请不要转换 malloc 的结果
  • malloc 的强制转换往往会掩盖代码中的其他问题。由于保证返回与请求的数据类型正确对齐,因此这种屏蔽是不可取的。
  • 在一个更幽默的注释中,有一个OOM杀手here的类比...

标签: c++ c memory-management


【解决方案1】:

默认情况下,Linux 通常使用机会性内存分配方案,这意味着内核会给你一个有效地址,直到第一次使用才会分配。

见:

根据这些回复,您可以使用 echo 2 &gt; /proc/sys/vm/overcommit_memory 关闭此功能。

据我所知,这是在假设您不一定会使用您分配的所有内存的情况下完成的。我不能说我个人曾经分配过至少一次不接触的空间,所以我很想知道这会如何影响现实生活中的表现......

关于 SIGKILL 失败,您调用的每个 malloc 仍在为每个调用分配一些内存。最终,您可能会用 malloc 开销填充您的内存,从而引起内存不足终止功能的愤怒。仅此一个问题,还是过度使用策略仍然分配了所请求空间的一部分,这是一个很好的问题。

【讨论】:

  • 啊,好吧,我想这回答了我的问题。但说实话 - 这真的很奇怪。如果这是几乎每个 linux 系统的默认设置,我为什么要检查 malloc 的结果是否为 NULL....
  • @rralf:因为不是每个系统都是Linux,也不是每个Linux系统都使用默认设置,也不能保证默认设置下不会返回NULL。
  • @rralf 因为也许明天 Linux 会被修改并删除这个“功能”?因为也许您的程序将在overcommit_memory 设置为2 的系统上运行?等等。 TL;DR:因为这是正确的事情。
  • @Bakuriu:从我目前所读到的内容来看,它似乎在假设你不会使用你分配的所有内存的情况下实现的。如果它认为你会使用它(现在或以后),那么批准过度分配没有多大意义,除非内核希望其他进程在现在和那时之间释放内存。它对于“理论上”复制父内存的进程分叉似乎很有用,但如果分叉的子进程从未接触过该内存,那么您根本不必复制它。无论如何,这就是我从到目前为止所阅读的内容中收集到的内容,但我还没有进行过广泛的研究。
  • @Anthony:一个例子是像一些虚拟机和服务器进程那样分配一个巨大的 1GB 临时工作区。您绝对不会一次全部使用它。您可能随时只需要几百 MB 物理支持的内存,其余的未使用或能够有效地移动到交换。请注意,Linux 不能保证 从不 返回 NULL;地址空间有一个上限,如果你用尽了所有这些,它就不能“伪造”任何东西。
【解决方案2】:

通常,Linux 会根据您的要求分配尽可能多的(虚拟)内存,并且仅在需要时为其分配物理内存。如果系统的物理内存用完,它就会开始杀死进程以释放一些。

这意味着malloc 将成功,除非请求是可笑的,但您的进程(或其他进程)可能会在使用内存时被杀死。

有关更多详细信息,请参阅malloc 的联机帮助页及其参考:

默认情况下,Linux 遵循乐观的内存分配策略。 这意味着当malloc() 返回非NULL 时,无法保证 内存真的可用。万一事实证明 系统内存不足,一个或多个进程将被 OOM杀手。有关详细信息,请参阅 /proc/sys/vm/overcommit_memory 和 /proc/sys/vm/oom_adj 在proc(5),和 内核源文件 Documentation/vm/overcommit-accounting。

(当然new 不会返回 null 除非您使用无抛出版本)。

【讨论】:

  • 多么愚蠢的错误设计。程序员如何检测请求的内存是否可用?如果内存不可用,杀死我的进程而不是简单地从 malloc() 返回 NULL 是非常愚蠢的。如果内存不足,如何向用户显示错误消息?我在 Windows 上测试了 rralf 的上述代码,它运行良好!没有崩溃,没有杀戮。当没有剩余内存时只返回 NULL 。 Linux 上的耻辱!
【解决方案3】:

如果请求的分配无法完成,malloc 返回NULL但也许你应该尝试从 heap 分配大量空间。

here

在 linux 上,调用 malloc() 时出现错误的唯一方法是 禁用内存过度使用。在常规的 linux 系统上,这是 只有方式 malloc() 返回 NULL。如果一个 icecast 过程达到 到了那一步,反正它已经完蛋了,它什么也做不了 有意义:任何源读取都将失败(refbuf alloc),任何日志打印 也会失败(printf 也使用 malloc),所以它还不如放弃并 调用 abort()。

【讨论】:

  • 是的,当我的系统内存不足时会发生这种情况?!但是我的进程没有返回NULL,而是被杀死了。而且.. Err.. malloc 从堆中分配空间?!
  • malloc 确实从堆中分配...
  • OP 想知道他们得到 SIGKILL 而不是 malloc 返回 NULL。
  • @CaptainObvlious 我知道,我已经编辑了我的答案。
  • @phoeagon:是的,现在更容易理解了。但这是一些邮件列表上的帖子。是否在任何地方指定?如果真的是这样,那我为什么要检查我的 malloc 是否返回了有效值,而它从不返回 NULL?
【解决方案4】:

如果操作系统让您的程序运行那么久,Malloc 将返回 NULL。但是在 malloc 有机会运行之前,操作系统会杀死你的进程。

就像如果它检测到您正在写入分配给您的进程的外部内存页面,它会杀死您的进程。

【讨论】:

    猜你喜欢
    • 2011-05-24
    • 2011-01-09
    • 2017-05-25
    • 2017-10-08
    • 2019-11-17
    • 2017-09-27
    • 1970-01-01
    • 1970-01-01
    • 2016-05-24
    相关资源
    最近更新 更多