Linux 上怎么会出现 OOM 情况(OOM 杀手背后的启发式方法)?

Linux 上怎么会出现 OOM 情况(OOM 杀手背后的启发式方法)?

我知道虚拟内存的概念。使用按需分页(取决于 vm.overcommit_memory),您可以分配比可用 RAM 更多的内存。除非您“触碰”页面,否则实际上不会发生任何事情。否则我猜是页面错误,然后物理内存将用于页面框架。但这在某种程度上意味着,如果系统内存紧张,它只会将最近使用的内容分页出来并正常工作。

怎么会需要终止进程呢?是因为 mlock() 占用了太多内存吗?垃圾太多后会触发 OOM 吗?或者换个问法:触发 OOM 终止程序背后的启发式方法到底是什么?

我读到你可以执行“echo 1 > memory.oom_control”或“echo -17 > /proc/PID/oom_adj”来禁用它。这有什么影响?机器可能会在一段时间内完全没有响应。但如果有问题的进程以某种方式检测到它没有取得进展,它也可以暂时停止消耗内存(那么快),最终一切都应该重新开始工作,还是我错了?

在我的场景中只有一个进程(具有巨大的内存缓存)。当然,该数据不是持久的,但我宁愿不重新启动该进程(并重新加载该数据)。

答案1

您似乎对虚拟内存和过度提交之间的关系感到困惑。

物理内存由计算机中的 RAM 芯片组成。物理上不可能永远使用同时使用的内存比物理内存多。但是,自 20 世纪 70 年代末以来,出现了虚拟内存,系统会将一些数据从物理内存移到磁盘,从而存储实际上未被使用的内存,以便物理内存可用于其他用途。当这种情况发生时,程序无法立即使用被交换出的数据;当它们试图这样做时,处理器将生成一个页面错误,导致操作系统将所需数据从交换加载回物理内存。

通过使用虚拟内存,可以使用的传输数据总量将扩展到物理内存加上交换空间的大小。虽然这允许系统同时运行更多程序,但内存量实际使用中永远不会超过总可用量虚拟的内存(即 RAM + 交换空间)

原则上,内核应该跟踪程序向其请求的内存量,并且一旦它的簿记系统告诉它所有内存都已用完,它就应该拒绝更多内存的请求已分配,尽管可能不是全部用过的。大多数其他内核都这样做,所以它们没有 OOM 杀手(因为它们不需要)。

但是,程序从内核分配内存的系统调用不是按字节分配内存,而是按更大的块分配内存。相比之下,大多数程序用于获取内存的实际实现(malloc()C 库函数)允许程序按字节分配内存。这意味着大多数程序在请求一些内存时,最终malloc()分配的内存会超过它们实际需要的内存。此外,一个好的malloc()实现将使用一些启发式方法来将较小和较大的分配分开,这样内存碎片就不会太严重,但这样做需要它从内核请求更多的内存,从而加剧问题。由于这些和其他影响,在没有过度承诺的情况下,将有大量的内存被分配,但将绝不使用。

过度承诺的想法是,你可以安全地分配一些比系统可用的虚拟内存总量更多的内存,而不会产生反作用。本质上,内核声称它允许分配,即使它知道它无法履行这一承诺,因为它认为它不需要完全履行这些承诺。但是,无法预测可以过度使用(分配)的确切数量,而不会出错;因此,当内核检测到程序试图使用分配给它们的所有内存时,内核将不得不违背其承诺。这就是 OOM 杀手发挥作用的地方。

这是否是个好主意还有待商榷。当系统内存不足时,某物会出错。如果某个应用程序试图分配内存不足,但内核已经保留了所有内存,那么该特定应用程序可能会崩溃。如果那恰好是您的 X 服务器,那么您就倒霉了。至少如果有 OOM 终止程序,它可以决定终止哪个进程,而不是在分配错误后出现问题;并且 OOM 终止程序具有一些逻辑来避免终止重要进程,例如您的 X 服务器...

但是,如果您愿意,您可以使用一些 sysctl 旋钮来关闭过度提交,如果您认为这是一个坏主意。

相关内容