默认关闭 Linux OOM 杀手?

Pet*_*aut 37 linux memory kernel oom

Linux 上的 OOM 杀手时常对各种应用程序造成严重破坏,而且似乎在内核开发方面并没有真正做很多事情来改进这一点。作为设置新服务器时的最佳实践,在内存过量使用时反转默认设置,即关闭它 ( vm.overcommit_memory=2) ,除非您知道需要将其打开以用于特定用途,这不是更好吗?这些用例是什么,你知道你想要过度使用?

作为奖励,由于情况下的行为vm.overcommit_memory=2取决于vm.overcommit_ratio和交换空间,因此调整后两者的大小以便整个设置保持合理工作的良好经验法则是什么?

An̲*_*rew 66

一个有趣的类比(来自http://lwn.net/Articles/104179/):

一家飞机公司发现,在飞机上用更少的燃料飞行会更便宜。飞机会更轻,使用更少的燃料,并节省金钱。然而,在极少数情况下,燃料量不足,飞机就会坠毁。该问题由该公司的工程师通过开发一种特殊的OOF(out-of-fuel)机制解决了。在紧急情况下,一名乘客被选中并被扔出飞机。(必要时,重复该程序。)发展了大量理论,许多出版物都致力于正确选择被驱逐的受害者的问题。应该随机选择受害者吗?还是应该选择最重的人?还是最老的?如果乘客付费以免被驱逐,使受害者成为船上最穷的人?例如,如果选择了最重的人,那么如果是飞行员,是否应该有一个特殊的例外?头等舱乘客是否应该豁免?既然存在OOF机制,它就会时不时地被激活,即使在没有燃料短缺的情况下也会弹出乘客。工程师们仍在精确研究这种故障是如何引起的。

  • 我非常喜欢它,谢谢你把它挖出来。 (12认同)

wom*_*ble 32

OOM 杀手只会在您的系统超载时造成严重破坏。给它足够的交换空间,不要运行突然决定吃掉大量内存的应用程序,你就不会有问题。

具体回答您的问题:

  • 我认为在一般情况下关闭过度使用不是一个好主意;很少有应用程序被编写为正确处理brk(2)(以及使用它的包装器,例如malloc(3))返回错误。当我在之前的工作中对此进行试验时,人们认为让一切能够处理内存不足错误比仅仅处理 OOM 的后果更麻烦(在我们的例子中,如果发生 OOM,这比不得不重新启动偶尔的服务要糟糕得多——我们不得不重新启动整个集群,因为 GFS 是一堆热气腾腾的粪便)。
  • 您希望对任何过度使用内存的进程进行过度使用。这里最常见的两个罪魁祸首是 Apache 和 JVM,但许多应用程序或多或少地这样做。他们认为在未来的某个时候他们可能需要大量内存,所以他们立即抢占了一大块。在启用了过量使用的系统上,内核会发出“嗯,无论如何,当您真正想要写入这些页面时来打扰我”并且没有任何不好的事情发生。在一个overcommit-off系统上,内核说“不,你不能有那么多内存,如果你碰巧在将来的某个时候写了所有的东西,我就被骨头化了,所以没有内存给你!” 并且分配失败。既然没什么那里显示“哦,好的,我可以拥有这么少量的进程数据段吗?”,然后进程要么 (a) 因内存不足错误退出,要么 (b) 不检查来自malloc,认为可以去,并写入无效的内存位置,导致段错误。值得庆幸的是,JVM 在启动时执行所有预分配(因此您的 JVM 要么立即启动,要么立即终止,您通常会注意到这一点),但 Apache 对每个新孩子都做了一些时髦的事情,这可能会在生产中产生令人兴奋的效果(不可复制的“不处理连接” ”类型的兴奋)。
  • 我不想将我的 overcommit_ratio 设置为高于默认值 50%。同样,从我的测试来看,虽然将其设置为 80 或 90 左右可能听起来很酷,但内核在不方便的时候需要大块内存,并且过载率高的满载系统可能没有足够的备用内存当内核需要它时(导致恐惧、瘟疫和糟糕)。因此,过度使用引入了一种新的、更有趣的故障模式——而不是在内存不足时重新启动任何出现 OOM 的进程,现在您的机器崩溃,导致机器上的所有内容中断。惊人的!
  • 无过度使用系统中的交换空间取决于您的应用程序需要多少请求但未使用的内存,以及健康的安全裕度。找出在特定情况下需要什么留给读者作为练习。

基本上,我的经验是关闭过度使用是一个很好的实验,但在实践中很少像理论上那样奏效。这很好地与我的经验对应与内核其他可调参数- Linux内核开发者几乎总是比你聪明的,默认的工作最适合广大,广大大多数情况下。别管他们,而是去找哪个进程有泄漏并修复它。

  • 我不希望我的备份进程因为有人对我的 Web 服务器进行 DoS 攻击而被终止。例外是可以的,但默认值应该是安全性和一致性。恕我直言,应该手动打开像 OOM 这样的优化。这就像编码一样,你编码干净,然后优化。过度提交是一个不错的功能,但不应成为默认功能。 (2认同)

小智 6

嗯,我并不完全相信支持过度使用和 OOM 杀手的论点......当 womble 写道,

“OOM 杀手只会在系统过载时造成严重破坏。给它足够的交换空间,不要运行突然决定吃掉大量 RAM 的应用程序,你就不会有问题。”

他是关于描述一个环境场景,其中不强制执行过量使用和 OOM 杀手,或者不“真正”采取行动(如果所有应用程序都根据需要分配了内存,并且有足够的虚拟内存可供分配,则内存写入将密切关注内存分配,而不是错误,因此即使启用了过度使用策略,我们也无法真正谈论过度使用的系统)。这是关于一种隐含的承认,即过度使用和 OOM 杀手在不需要干预时效果最佳,据我所知,该策略的大多数支持者以某种方式分享了这一点(我承认我不能说太多......)。Morover,在预分配内存时提到具有特定行为的应用程序让我认为可以在分发级别调整特定处理,而不是具有默认值,

对于什么关注的JVM,那么,它是一个虚拟机,它在某种程度上需要分配所有IT资源需要在启动时,所以它可以为它的应用程序创建的“假”的环境,并保持其可用资源从主机中分离环境,尽量。因此,最好让它在启动时失败,而不是在一段时间后由于“外部”OOM 条件(由过度使用/OOM 杀手/其他原因引起),或者无论如何遭受这种干扰它自己的条件内部 OOM 处理策略(一般来说,VM 应该从一开始就获得任何所需的资源,而主机系统应该“忽略”它们直到结束,同样的,与显卡共享的任何数量的物理内存永远不会 - 也不能- 被操作系统触及)。

关于 Apache,我怀疑让整个服务器偶尔被杀死并重新启动比让一个孩子和一个连接从它的(=孩子的/连接的)开始失败(好像它是一个全新的实例)更好在另一个实例运行一段时间后创建的 JVM)。我想最好的“解决方案”可能取决于特定的上下文。例如,考虑到电子商务服务,有时与购物图表的一些连接随机失败而不是丢失整个服务可能会更好,例如,有中断正在进行的订单完成的风险,或者(也许更糟)一个付款过程,以及案件的所有后果(可能无害,但可能有害——当然,当问题出现时,

同样,在工作站上,消耗最多资源的进程,因此成为 OOM 杀手的首选,可能是内存密集型应用程序,例如视频转码器或渲染软件,可能是唯一的应用程序用户希望保持原状。这种考虑提示我 OOM 杀手默认策略过于激进。它使用一种“最适合”的方法,在某种程度上类似于某些文件系统的方法(OOMK 尝试并释放尽可能多的内存,同时减少被杀死的子进程的数量,以防止在短时间内进行任何进一步的干预,如以及 fs 可以分配比某个文件实际所需更多的磁盘空间,以防止在文件增长时进行任何进一步分配,从而在某种程度上防止碎片)。

但是,我认为相反的策略,例如“最适合”的方法,可能更可取,因此可以释放某个点所需的确切内存,而不是打扰“大”进程,这很可能会浪费内存,但也可能不知道,并且内核不知道(嗯,我可以想象保持跟踪页面访问计数和时间可以暗示进程是否正在分配它不再需要的内存,因此猜测进程是否正在浪费内存或只是使用了很多,但访问延迟应该在 cpu 周期上加权,以区分内存浪费与内存cpu 密集型应用程序,但是,虽然可能不准确,但这种启发式方法可能会产生过多的开销)。

此外,杀死较少可能的进程总是一个不错的选择可能并不正确。例如,在桌面环境中(例如,让我们考虑资源有限的上网本或上网本),用户可能正在运行具有多个选项卡的浏览器(因此,内存消耗 - 假设这是 OOMK 的首选) ,加上一些其他应用程序(带有未保存数据的文字处理器、邮件客户端、pdf 阅读器、媒体播放器等),加上一些(系统)守护程序,以及一些文件管理器实例。现在,发生 OOM 错误,OOMK 选择在用户通过网络执行被认为“重要”的事情时杀死浏览器……用户会感到失望。另一方面,关闭少数文件管理器'

无论如何,我认为应该让用户自己决定要做什么。在桌面(=交互式)系统中,如果保留足够的资源来要求用户关闭任何应用程序(但即使关闭几个选项卡就足够了)并处理他的选择(一个选项可以包括创建一个额外的交换文件,如果有足够的空间)。对于服务(以及一般而言),我还考虑了两个进一步的可能增强功能:一个是记录 OOM 杀手干预,以及进程启动/分叉失败的方式可以轻松调试失败(例如,API 可以通知发出新进程创建或分叉的进程 - 因此,像 Apache 这样的服务器,带有适当的补丁,可以为某些错误提供更好的日志记录);这可以独立于过度使用/OOMK 进行;其次,但不重要的是,可以建立一种机制来微调 OOMK 算法 - 我知道在某种程度上,可以在逐个流程的基础上定义特定的策略,但我的目标是“集中式”配置机制,基于一个或多个应用程序名称(或 ID)列表来识别相关进程并赋予它们一定程度的重要性(根据列出的属性);这种机制应该(或至少可以)是分层的,这样就可以有一个顶层用户定义的列表、一个系统(分布)定义的列表和(底层)应用程序定义的条目(所以例如,DE 文件管理器可以指示 OOMK 安全地杀死任何实例,

此外,可以提供 API 以允许应用程序在运行时提高或降低其“重要性”级别(关于内存管理目的和执行优先级),例如,字处理器可以从低“重要性”但在刷新到文件之前保留某些数据或正在执行写入操作时会升高它,一旦此类操作结束,重要性再次降低(类似地,文件管理器可以在它从刚刚传递时改变级别点燃文件来处理数据,反之亦然,而不是使用单独的进程,Apache 可以为不同的子进程赋予不同级别的重要性,或者根据系统管理员决定并通过 Apache 或任何其他类型的服务器公开的某些策略更改子状态- 设置)。当然,这样的 API 可能并且会被滥用/误用,但我认为与内核任意杀死进程以释放内存而没有任何有关系统正在发生的事情的相关信息(以及内存消耗/创建时间或类似信息)相比,这是一个小问题'对我来说不够相关或'验证') - 只有用户、管理员和程序编写者才能真正确定某个进程是否出于某种原因“仍然需要”,原因是什么,和/或应用程序是否处于领先状态如果被杀,数据丢失或其他损坏/麻烦;然而,还可以做出一些假设,例如查找某个进程获取的某种资源(文件描述符、网络套接字等),并且有待处理的操作可以判断一个进程是否应该处于比它更高的“状态”一套,

或者,只是避免过度使用,让内核做内核必须做的事情,分配资源(但不要像 OOM 杀手那样任意抢救它们)、调度进程、防止饥饿和死锁(或从中抢救)、确保完全抢占和内存空间分离,等等...

我还会多说一些关于过度使用方法的内容。从其他讨论中,我提出了一个想法,即过度使用的主要担忧之一(既是想要它的原因,也是可能出现问题的根源)包括分叉处理:老实说,我不知道副本到底是如何-实现了写时策略,但我认为任何激进(或乐观)的策略都可以通过类似交换的位置策略来缓解。也就是说,不仅仅是克隆(和调整)一个分叉的进程代码页和调度结构,还可以在实际写入之前复制一些其他数据页,在父进程访问的那些页面中选择更频繁地写入(即,使用计数器进行写操作)。

一切,当然,恕我直言。

  • “此外,可以提供 API 以允许应用程序在运行时提高或降低其‘重要性’级别” 重要性是`/proc/$PID/oom_adj`。 (5认同)
  • @维。现在似乎是`/proc/$PID/oom_score_adj` (4认同)