处理“内存不足”的正确方法是什么?
最近,我在Windows上制作了一个CCTV节目的video播放器程序。 由于程序必须同时解码和播放许多video流,我认为它可能会遇到malloc失败的情况,并且我在每个malloc之后添加检查。
但一般来说,在我在开源项目中阅读的这些开源程序代码中,我很少发现对malloc结果的任何检查。 所以当malloc失败时,大多数程序都会崩溃。 那不是不接受吗?
我在linux上编写服务器程序的同事会为100个客户端连接分配足够的内存。 因此,虽然他的程序可能会拒绝101客户端,但它永远不会遇到malloc的失败。 他的方法是否也适合桌面应用程序?
在Linux上, malloc()
永远不会失败 – 相反,OOM杀手将被触发并开始杀死随机进程,直到系统崩溃。 由于Linux是当今最流行的UNIX衍生产品,许多开发人员已经学会了永远不检查malloc()
的结果。 这可能是你的同事忽略malloc()
失败的原因。
在支持失败的操作系统上,我看到了两种一般模式:
-
编写一个自定义过程来检查
malloc()
的结果,并在分配失败时调用abort()
。 例如, GLib和GTK +库使用这种方法。 -
存储“可清除”分配的全局列表,例如缓存,可在分配失败时清除。 然后,再次尝试分配,如果仍然失败,则通过标准错误报告机制(不执行动态分配)报告。
遵循标准化API
即使在Linux上,ulimit也可用于获取malloc错误返回的提示。 它只是默认为无限制。
符合公布的标准存在明确的压力。 在大多数系统上,从长远来看,甚至在Linux上, malloc(3)
都会返回正确的失败指示。 桌面系统确实具有虚拟内存和请求分页,但即使这样,检查malloc(3)
只能在没有内存泄漏的调试程序中运行。 如果出现任何问题,有人会想要设置ulimit
并跟踪它。 突然, malloc
检查是有道理的。
在没有检查null的情况下使用malloc的结果在可能在malloc可能失败的平台上开放使用的代码中是不可接受的,在这些代码上它往往会导致崩溃和不可预测的行为。 我不能预见未来,不知道我的代码将去哪里,所以我会编写代码,检查malloc返回null – 更好地死,而不是表现得不可预测!
如果malloc失败,该怎么做的策略取决于应用程序的类型以及您对所使用的库的信心。 在某些情况下,唯一安全的做法是停止整个程序。
如果您的应用程序的内存使用是可预测的,那么预先分配已知的内存量并在某些块中进行分配的想法,因此转向实际上耗尽了内存是一个很好的。 您可以将其扩展为编写自己的内存管理例程以供代码使用。
这取决于您正在处理的应用程序类型。 如果应用程序的工作分为可以允许单个任务失败的离散任务,那么可以优雅地恢复检查内存分配。
但在许多情况下,响应malloc失败的唯一合理方法是终止程序。 允许您的代码在不可避免的空取消引用上崩溃将实现这一点。 转储日志条目或解释错误的错误消息肯定会更好,但在现实世界中,我们的工作时间有限。 有时迂腐error handling的投资回报不存在。
始终检查并预先分配可在此情况下释放的缓冲区,以便警告用户保存其数据并关闭应用程序。
取决于你写的应用程序。 当然,您总是需要检查malloc()的返回值。 但是,优雅地处理OOM仅在某些情况下才有意义,例如低级关键系统服务,或者编写可能使用的库时。 因此,在许多应用程序和框架中使用在OOM上中止的malloc包装器非常常见。 这些包装器通常被命名为xmalloc()或类似的。
GLib的g_malloc()也正在中止。
如果你要处理大量内存,并希望向Linux发表声明,例如“现在我有内存区ABC,我不需要B片,按你的意愿做”,看看mmap()/ madvise()库存GNU C库中可用的函数系列。 根据您的使用模式,代码最终可能比使用malloc更简单。 此API还可用于通过缓存您将要读/写一次的文件来帮助Linux不浪费内存。
它们在GNU libc info文档中有很好的文档。