当内核使用过度使用内存时,是不是需要在分配内存后检查 NULL

Posted

技术标签:

【中文标题】当内核使用过度使用内存时,是不是需要在分配内存后检查 NULL【英文标题】:Is there a need to check for NULL after allocating memory, when kernel uses overcommit memory当内核使用过度使用内存时,是否需要在分配内存后检查 NULL 【发布时间】:2011-01-15 23:31:22 【问题描述】:

通常的做法是在 malloc() 之后检查 NULL(内存是否分配成功),例如

void *ptr = malloc(10);    
if (ptr != NULL)   
  // do some thing usefull  
 else   
 // no memory. safely return/throw ...  
  

在内核中启用内存过量使用后,是否有机会获得 NULL?我应该遵循虔诚地检查每个分配的 NULL 的做法吗?尽管有积极的过度使用机制,malloc 会返回 NULL(我猜值为 1)吗?

事实上,android内核使用内存过度使用(不确定值,很想知道它(过度使用值)及其意义)。 Android(可能是第 3 方)中的一些框架源代码(C/C++)代码在分配后不会检查 NULL 也不会捕获 bad_alloc。我错过了什么吗?

SO 中有一些关于过度使用内存的线程,但没有一个能解决我的困惑。

编辑: 如果正在使用积极的过度使用,则不会返回 NULL(假设 1)。当没有可用的物理内存并且尝试访问分配的内存(写入分配的内存)时,OOM 将终止一些进程并为应用程序分配内存,直到它被依次终止(假设 2)。在任何一种情况下,我都认为不需要检查 NULL(内存被分配或进程被杀死)。 我的假设是否正确?可移植性不是这个问题的关注点。

【问题讨论】:

什么是overcommit memory?如果您在谈论设备上的页面文件,那么不,Android(和其他移动设备)通常不使用页面文件。这是因为写入会更快地耗尽 SSD 的 NAND 或闪存。还是您在谈论模拟器和虚拟机管理程序? 使用 OOM-Killer 和 Memory Overcommit,在 malloc 之后检查空指针不再是必要的并且会产生误导。 【参考方案1】:

是的,您仍应检查 malloc 返回的故障。在过度使用内存的环境中,您将无法检测到故障并从故障中恢复,因为当您写入先前调用 @987654322 分配给程序的部分地址空间时,环境耗尽所需的物理存储空间@。

但是,这并不是导致malloc 在传统环境中失败的唯一问题。当程序的地址空间变得碎片化时,即使有足够的总物理内存来满足请求,对特别大的内存块的请求也可能会失败。因为没有连续范围的可用地址空间malloc 必须失败。这种类型的故障必须由malloc 返回NULL 发出信号,无论环境是否过度使用内存。

【讨论】:

如果您尝试分配 1M 的内存并且 malloc 由于碎片而返回 NULL,您将如何处理这种情况?如果您由于错误而尝试分配大量内存怎么办,您宁愿崩溃并获得核心转储而不是干净地退出或以其他方式解决错误吗? @Skrymsli:这取决于应用程序和环境。如果需要立即“崩溃”,那么我更喜欢明确的abort,而不是继续运行不确定的时间,直到使用空指针导致崩溃作为副作用。一些应用程序可能能够清除大量分配的内存并以某种方式恢复。但是,问题的前提是,我是否应该检查 null 因为,当然,过度提交永远不会发生,我声称这是不正确的。【参考方案2】:

您必须每次检查返回值是否为NULL。任何库函数都可能失败。甚至 fclose() 也会这样做(在断开连接的 NFS 共享上,并且来自 NFS 文件的 fclose 错误意味着没有保存该数据)。

大部分软件都写得不好,并且没有包含所有的检查。

malloc 不能返回 NULL 或指针以外的东西。全有或全无。如果您要求 10,则无法从 malloc 获得 1 个字节。

【讨论】:

我的问题不是malloc的实现,而是malloc的使用。启用内存过量使用后,获得 NULL 的机会(理论上)非常小,这在一些 android c/c++ 框架源中很明显 - 在分配后不会进行此检查。 过度使用 mallic 将进一步失败。 opsmonkey.blogspot.com/2007/01/linux-memory-overcommit.html 一般情况下,malloc 不会失败,当内存可用且未达到 ulimit 时 "过度使用 malloc 会失败得更远。"我认为这是错误的假设,请查看您提到的链接。它说demo1:malloc内存并且不使用它:分配1.4TB,被OOM杀手杀死 demo2:malloc内存并立即使用它:分配7.8GB,被OOM杀手杀死(进程被OOM杀手杀死而不是malloc的情况失败)。 demo1 - 分配内存并且不使用 这个分配的内存 - 所以过度使用会起作用,页面不会真正分配。 demo2 - 分配内存并将 memset 分配给 0 (这将把这个内存从填满 0 的写保护零页重新映射到实际内存,因此它会禁用过度使用) 它在典型的 Android 机器上启用了交换吗? 【参考方案3】:

建议在所有可能返回 NULL 的函数调用中认真检查 NULL,无论内核是否具有过度提交的内存。

以下代码段显示了如何检查对malloc 的调用是否有效...

无效 *ptr = malloc(10); 如果(指针!= NULL) /* 在这里用 ptr 做一些事情 */ 别的 /* 如果失败在这里做一些事情 */

文件操作、内存操作不胜枚举,但少数会在失败时返回 NULL。

希望这会有所帮助, 最好的祝福, 汤姆。

【讨论】:

'代码段是一个危险的假设'你能解释一下吗? 'Android 中间件' 很抱歉如此通用,'android 中的一些框架工作源(C/C++)代码' 我应该说。我将编辑我的问题。 也许这是题外话,但我很好奇您在 /* 如果失败,请在此处执行操作 */ 部分采取什么措施?据推测,如果您真的无法分配 10 字节的内存,那么无论您做什么都将无法分配内存。您可能会进入检查内存不足并处理它的无限循环。您是否退出?这比以某种方式在 null 取消引用上崩溃更好吗?【参考方案4】:

嗯...在 Linux 上,因为内存不是页面支持(最初)并且仅在第一次读/写后创建页面支持,操作系统将始终成功地为您提供内存(除非您用尽了地址空间,这是不可能的64 位系统)。因此,如果它耗尽内存并且无法为您提供承诺的内存,OOM 杀手只会杀死您的应用程序或其他一些应用程序,以便为您提供所需的页面支持。所以不管你是否做NULL检查,结果都是一样的,一个crash.......

【讨论】:

【参考方案5】:

不,不需要检查malloc的结果。

早在 malloc 失败之前,操作系统就已经遇到了很多问题。

“OOM-Killer and overcommit”会是更好的选择。

什么?您的操作系统不支持“OOM-Killer and overcommit”?

这就是你应该切换到 Linux(或 Android)的原因!

【讨论】:

你的回答是不相关的咆哮。我去掉了一半。你的回答也不好,所以我投了反对票。 我不知道为什么这个网站允许你编辑我的帖子。但是使用 OOM-Killer 和 Memory Overcommit,在 malloc 之后检查空指针不再是必要的,并且会产生误导。 请参阅***.com/faq#editing。您也可以撤消我的更改……对于最初的问题:确实,现代操作系统为进程分配 RAM 是惰性的并过度使用。并且当内存真正被访问时,它会被分配。也许有人必须在那时得到OOM'd。但这不是问题。 malloc 调用 brk 急切地增加它的堆。操作系统我拒绝该请求。在这种情况下 malloc 将返回 NULL。例如。 ulimit 可以限制相关进程的最大虚拟内存大小。 关于内存管理的最佳实践是在恶意 malloc 真正发生之前“防止”它。如果邪恶的 malloc 发生,没有任何帮助。

以上是关于当内核使用过度使用内存时,是不是需要在分配内存后检查 NULL的主要内容,如果未能解决你的问题,请参考以下文章

当进程使用 shm_open() 时,Linux 内核如何分配内存指针?

通过过度分配内存在结构中内联可变长度数组是不是有效?

.NET 可用内存使用情况(如何防止过度分配/释放内存给操作系统)

oom killer

oom-killer, 杀掉进程的凶手

当向量需要更多内存并分配内存时,指针会发生啥?