我可以依靠 malloc 返回 NULL 吗?

Posted

技术标签:

【中文标题】我可以依靠 malloc 返回 NULL 吗?【英文标题】:Can I rely on malloc returning NULL? 【发布时间】:2011-12-18 08:53:58 【问题描述】:

我读到在 Unix 系统上,malloc 可以返回一个非 NULL 指针,即使内存实际上不可用,并且稍后尝试使用内存会触发错误。由于我无法通过检查 NULL 来捕获这样的错误,我想知道检查 NULL 到底有多大用处?

在相关的说明中,Herb Sutter 说处理 C++ 内存错误是徒劳的,因为系统会在异常实际发生之前很久就进入分页痉挛状态。这是否也适用于malloc

【问题讨论】:

我认为你不应该在 C++ 中使用 malloc :***.com/questions/184537/… @lc2817 只有在使用 C 接口编写代码时才应该使用 malloc(即,要从 C 中使用但用 C++ 编写的函数) C 代码负责释放该内存。 @WTP 感谢您的精确度。虽然,我不知道这里是不是这样。 @Dror K.,我不明白赏金的目的,快速谷歌搜索没有帮助。该问题已经有了答案,是否意味着您正在寻找另一个改进的答案? @gsamaras 您好,我选择了表明现有答案值得赏金的选项。所以你的问题的答案是我想奖励一个现有的答案,我不是在寻找新的答案。 【参考方案1】:

引用Linux manuals:

默认情况下,Linux 遵循乐观的内存分配策略。这意味着当malloc() 返回非NULL 时,没有 保证 内存真的可用。这是一个非常糟糕的错误。如果发现系统内存不足,一个或多个 过程将是 被臭名昭著的OOM杀手杀死。如果在不太希望突然丢失的情况下使用 Linux 一些随机的 选择的进程,而且内核版本足够新,可以关闭这种过度使用行为 使用如下命令:

# echo 2 > /proc/sys/vm/overcommit_memory

您应该检查NULL 返回,尤其是在 32 位系统上,因为进程地址空间可能会在 RAM 之前耗尽:例如,在 32 位 Linux 上,用户进程可能有 2G 的可用地址空间- 3G 而不是超过 4G 的总 RAM。在 64 位系统上,检查 malloc 返回码可能没用,但无论如何可能被认为是一种好的做法,它确实使您的程序更具可移植性。而且,请记住,取消引用空指针肯定会杀死您的进程;与此相比,一些交换可能不会造成太大伤害。

如果malloc 碰巧在尝试分配少量内存时返回NULL,那么在尝试从错误条件中恢复时必须小心,因为任何后续malloc 也可能失败,直到足够内存可用。

默认的 C++ 运算符 new 通常是 malloc() 使用的相同分配机制的包装器。

【讨论】:

+1 引用了一篇关于 Linux 默认值如何损坏的好言论。一个好的程序应该总是检查malloc 的返回值。如果用户错误地配置了他们的系统(或将其保留在损坏的默认配置中),那么这当然可能无济于事,但是您无能为力,并且崩溃不在您的责任范围内。但是如果你没有检查malloc 的返回值,你的程序会在用户/管理员真正关心正确性并且禁用了过度提交的系统上运行时中断。然后用户可能会认为你的程序是垃圾。 :-) 嗯,事实比这要复杂一些。进程地址空间有漏洞;例如,程序可能永远不会触及 BSS 中的所有页面,或者更改数据段中映射的页面。在桌面/服务器系统上,与过度使用相比,使用不足通常是一个更大的问题。如果启用交换分区,也可以在事情变得非常糟糕之前提供一些缓冲。 我不同意。 Undercommit 不是问题,因为您总是可以向它抛出更多交换。在任何情况下,如果您有未触及的 bss/数据页面,这意味着您有全局变量(不仅仅是 GOT/PLT),这是一个更大的问题。 :-) 也许一些是必要的,但超过一两页的价值几乎肯定表明设计问题...... 对新手友好的系统 ;) 我唯一一次不得不处理 OOM 杀手将是一个失控的进程,无论如何都会通过交换导致系统停止运行。【参考方案2】:

在 Linux 上,如果由于内核的过度分配策略导致没有足够的内存可用,您确实不能依赖 malloc 返回 NULL,但您仍然应该检查它,因为在某些情况下 malloc 返回NULL,例如当您要求的内存超出机器的总可用内存时。 Linux malloc(3) 手册页将过度分配称为“一个非常糟糕的错误”,并包含有关如何关闭它的建议。

我从未听说过这种行为也发生在其他 Unix 变体中。

至于“分页痉挛”,这取决于机器设置。例如,我倾向于不在笔记本电脑 Linux 安装上设置交换分区,因为您担心的确切行为可能会杀死硬盘。我仍然希望我运行的 C/C++ 程序检查 malloc 返回值,给出适当的错误消息,并在可能的情况下自行清理。

【讨论】:

严格来说,过度使用既不是功能也不是错误。这只是历史上的懒惰:过度提交比考虑提交费用更容易实现。大概有些人已经习惯并喜欢它(出于任何不正当的原因),有些人甚至开始编写将malloc 1gb 作为稀疏数组甚至更不正当的东西的程序,所以现在我们一直坚持下去 -默认...【参考方案3】:

换个角度看:

malloc即使内存实际上不可用也可以返回非NULL指针”并不意味着它总是返回非NULL。可能(并且将会)返回 NULL 的情况(正如其他人已经说过的那样),所以这个检查仍然是必要的。

【讨论】:

【参考方案4】:

检查malloc 的返回值本身并不能帮助您确保分配更安全或更不容易出错。如果这是您实施的唯一测试,它甚至可能是一个陷阱。

当使用0 的参数调用时,标准允许malloc 返回一种唯一地址,该地址不是空指针,但您无权访问。因此,如果您只测试返回值是否为0,但不测试malloccallocrealloc 的参数,您可能会在很久以后遇到段错误。

这种错误情况(内存耗尽)在“托管”环境中非常罕见。通常,在您为此类错误烦恼之前很久,您就遇到了麻烦。 (但如果你正在编写运行时库,是内核黑客还是火箭制造者,情况就不同了,那里的测试非常有意义。)

然后人们倾向于用跨越多行的复杂捕获错误条件来装饰他们的代码,例如perror 和类似的东西,这可能会影响代码的可读性。

我认为这种“检查malloc 的返回”被高估了,有时甚至被相当教条地辩护。其他更重要的事情:

总是初始化变量,总是。对于指针变量,这是至关重要的, 让程序在事情变得太糟糕之前很好地崩溃。 structs 中未初始化的指针成员是导致难以发现的错误的重要原因。 总是检查malloc 和 Co. 的参数,如果这是一个编译 像sizof toto 这样的时间常数不会有问题,但是 始终确保您的向量分配正确处理零情况。

检查malloc 是否返回的一个简单方法是用memset(malloc(n), 0, 1) 之类的东西来包装它。这只是在第一个字节中写入一个0,如果malloc 有错误或n0 开始时会很好地崩溃。

【讨论】:

让我们说告诉用户“第 foo 行的堆外”比“bar 的空指针异常”要好得多;对于它来说,malloc 的简单(宏?)包装器就足够了。如果确实使用了荒谬的内存量并且可以预期在 32 位系统上使用超过 2G 的内存。

以上是关于我可以依靠 malloc 返回 NULL 吗?的主要内容,如果未能解决你的问题,请参考以下文章

使 malloc() 返回 NULL 而不是使程序崩溃?

C API设计:malloc返回NULL怎么办?

free函数和malloc函数

new和malloc申请内存失败后的处理

Java ManagedBean 方法可以返回 null 吗?

new和malloc的区别