Linux上的OOM杀手每隔一段时间就会对各种应用程序造成严重的破坏,看起来在内核开发方面做的并不多。 作为设置新服务器的最佳实践,如果不想让内存过度使用,也就是将其closures( vm.overcommit_memory=2
) ? 那么这些用例会是什么,你知道你想要过度使用吗?
作为奖励,因为vm.overcommit_memory=2
的行为取决于vm.overcommit_ratio
和swap空间,那么select后两者的大小是一个很好的经验法则,这样整个设置才能合理地工作。
一个有趣的比喻(来自http://lwn.net/Articles/104179/ ):
一家航空公司发现,用更less的燃料在飞机上驾驶飞机便宜。 飞机会更轻,使用更less的燃料,节省了金钱。 然而,在极less数情况下,燃油量不足,飞机就会坠毁。 这个问题是由公司的工程师通过开发一种特殊的OOF(燃料外)机制解决的。 在紧急情况下,一名乘客被选中并被抛出飞机。 (必要时,重复这一程序。)大量理论得到发展,许多出版物专门讨论适当select被驱逐者的问题。 应该随机select受害者吗? 还是应该select最重的人? 还是最古老的? 旅客为了不被驱逐而付费,以便受害者成为最穷的乘客? 如果例如最重的人被选中,那么飞行员是否应该有一个特殊的例外呢? 第一类乘客是否应该豁免? 现在OOF机制已经存在了,时不时会被激活,甚至在没有燃料短缺的情况下也会排出乘客。 工程师们仍然在研究这种故障是如何造成的。
如果你的系统超载,OOM杀手只会造成严重的后果。 给它足够的交换空间,不要运行突然决定吃大量内存的应用程序,这样你就不会有问题了。
具体回答你的问题:
brk
(2)(以及使用它的包装器,如malloc
(3))会返回一个错误。 当我在以前的工作中对此进行了实验时,让所有能够处理内存不足错误的东西都被认为是一件麻烦事,而不是处理OOM的后果(在我们的例子中,要比发生OOM时重启偶尔的服务要糟得多 – 我们不得不重启整个集群,因为GFS是一堆大量的粪便)。 基本上,我的经验是closuresovercommit是一个很好的实验,在理论上听起来很less在实践中奏效。 这与我在内核中的其他可调参数的经验很好地相符 – Linux内核开发人员几乎总是比你更聪明,而且对于绝大多数情况,默认值都是最好的。 让他们一个人,而是去find哪个过程有泄漏,并修复它。
嗯,我不完全相信支持过度使用和OOM杀手的论据…当womble写道,
“如果你的系统超载,OOM杀手只会造成严重破坏,给它足够的交换空间,不要运行那些突然决定吃大量内存的应用程序,这样你就不会有问题。
他描述的是一个环境场景,其中overcommit和OOM杀手没有被强制执行,或者没有“真正”行为(如果所有的应用程序根据需要分配了内存,并且有足够的虚拟内存来分配,内存写入将紧跟内存分配错误,所以即使启用了overcommit策略,我们也不能真正谈论过度的系统)。 这是暗示承认过度使用和OOM杀手在不需要干预的情况下效果最好,据我所知(我承认我说不上多less……),这种战略的大多数支持者都有某种共同点。 Morover在预先分配内存时指的是具有特定行为的应用程序,这使得我认为可以在分布级别上调整特定的处理,而不是基于启发式的默认的系统方法(个人而言,我认为heuistic不是一个很好的方法对于内核的东西)
对于JVM来说,这是一个虚拟机,它在一定程度上需要分配启动时所需的全部资源,这样就可以为其应用程序创build“假”环境,并保持其可用资源与主机分离环境,尽可能。 因此,可能最好让它在启动时失败,而不是由于“外部”OOM状态(由过度使用/ OOM杀手/任何引起的)造成的一段时间,或者由于这种干扰自己的状况内部的OOM处理策略(一般来说,虚拟机应该从一开始就获得所需的资源,主机系统应该“忽略”它们,直到最后,与graphics卡共享任何数量的物理内存永远不会 – 也不可能 – 由操作系统触摸)。
关于Apache,我怀疑整个服务器有时会被杀死并重新启动,比让一个孩子连同一个连接从它(=孩子/连接的)开始失败(好像它是一个全新的实例)在另一个实例运行一段时间后创build的JVM)。 我想最好的“解决scheme”可能取决于具体的情况。 例如,考虑到电子商务服务,有时可能会有一些与购物图的连接失败而不是随意丢失整个服务的风险,例如中断正在进行的订单最终确定,或者(也许更糟糕)一个付款过程,与案件的所有后果(也许是无害的,但也许是有害的 – 当然,当问题出现,这些会更糟,那么为了debugging的目的,一个不可重现的错误条件)。
同样,在工作站上,消耗最多资源的过程,以及成为OOM杀手的首选的过程,可能是一个内存密集型应用程序,例如video代码转换器或渲染软件,可能是唯一的应用程序用户希望不受干扰。 这个考虑因素暗示了OOM杀手的默认策略过于激进。 它使用了一种“最差配合”的方法,它与某些文件系统类似(OOMK尝试尽可能多地释放内存,同时减less被杀死的subprocess的数量,以便在短时间内防止任何进一步的干预)以及fs可以分配更多的磁盘空间,然后真正需要某个文件,以防止任何进一步的分配,如果文件增长,从而防止碎片,在某种程度上)。
不过,我认为一个相反的政策,比如“最合适”的做法,可能更可取,因此可以在某一时刻释放所需的确切记忆,而不会受到“大”进程的困扰,这很可能是在浪费内存,但也可能不会,内核不知道,(嗯,我可以想象保持页面访问跟踪计数和时间可以暗示,如果一个进程分配内存不再需要,所以猜测一个进程是浪费内存或者只是使用很多,但访问延迟应加权CPU周期以区分内存和 CPU密集型应用程序的内存浪费,但可能不准确,这种启发式可能会有一个额外的开销)。
而且,杀死更less可能的过程总是一个好的select。 例如,在一个桌面环境中(让我们考虑一个资源有限的上网本或上网本),用户可能会运行带有多个选项卡的浏览器(因此,内存消耗 – 让我们假设这是OOMK的第一select) ,还有一些其他的应用程序(一个文件处理器,没有保存的数据,一个邮件客户端,一个PDF阅读器,一个媒体播放器等等)以及一些(系统)守护进程,以及几个文件pipe理器实例。 现在,一个OOM错误发生,OOMKselect杀死浏览器,而用户在网上做一些被认为是“重要”的东西,用户会感到失望。 另一方面,closures几个文件pipe理器的实例处于空闲状态可以释放所需的确切数量的内存,同时保持系统不仅工作,而且以更可靠的方式工作。
无论如何,我认为应该让用户自己决定要做什么。 在一个桌面(=交互式)系统中,这应该是相对容易的,只要提供足够的资源来请求用户closures任何应用程序(但是即使closures一些标签也足够了),并处理他的select包括创build一个额外的交换文件,如果有足够的空间)。 对于服务(一般来说),我还会考虑另外两个可能的增强function:一个是loggingOOM杀手干扰,另一个是启动/分支失败,这样可以很容易地debugging失败(例如,一个API可以通知进程发出新的进程创build或分叉 – 因此,像Apache这样的服务器,有一个适当的补丁,可以提供更好的日志logging某些错误)。 这可以从过度使用/ OOMK努力中独立完成; 其次,但并不重要,可以build立一种机制来微调OOMKalgorithm – 我知道在某种程度上可以在stream程基础上定义一个特定的stream程策略,但我会瞄准“集中式”configuration机制,基于一个或多个应用程序名称(或ID)列表来标识相关进程并给予它们一定程度的重要性(按照列出的属性); 这样的机制应该(或者至less可以)也是分层的,以便可以有最高级别的用户定义列表,系统(分配)定义列表和(底层)应用程序定义的条目(如此例如,DE文件pipe理器可以指示OOMK安全地杀死任何实例,因为用户可以安全地重新打开它来访问丢失的文件视图 – 而任何重要的操作(诸如移动/复制/创build数据)都可以委托给一个更“特权”的过程)。
Morover可以提供一个API,以允许应用程序在运行时(相对于内存pipe理目的而言,无论执行优先级)boost还是降低“重要性”级别,例如,一个Word处理器可以从一个低的“重要性”,但随着一些数据在刷新到文件之前被保持,或者正在执行写操作,并且一旦这样的操作结束时再次降低重要性(类似地,文件pipe理器可以在从引用文件来处理数据,反之亦然,而不是使用单独的进程,并且Apache可以给不同的孩子以不同的重要级别,或者根据由系统pipe理员决定并通过Apache公开的策略来改变孩子状态 – 或者任何其他种类的服务器 – 设置)。 当然,这样一个API可能会被滥用/被滥用,但是我认为这是一个小问题,与内核任意处理内存而没有任何有关系统的相关信息(以及内存消耗/创build时间或者对我来说,这些都是不够的相关性或“validation”) – 只有用户,pipe理员和程序编写人员才能真正确定某个过程是否由于某种原因而被“需要”,原因是什么以及/或者应用程序是否在导致数据丢失或其他损坏/麻烦的状态; 然而,还有一些假设可以做出来,例如寻找一个进程获得的某种types的资源(文件描述符,networking套接字等等),并且有待处理的操作可以判断一个进程是否应该处于比状态更高的状态一个集合,或者如果它的“自我build立的”比所需要的更高并且可以被降低(积极的方法,除非被用户的select取代,例如强制某个状态,或者通过我上面提到的列表 – 要求尊重应用程序的select)。
或者,只要避免过度使用,让内核做内核必须做的事情,分配资源(但不能像OOM杀手那样任意拯救它们),调度进程,防止死亡和死锁(或者从中抢救出来),确保完全抢占和内存空间分离等等…
我还会花更多的关于overcommit方法的话。 从其他的讨论中我发现,过度使用(作为需要它的一个理由和作为可能的麻烦来源)的主要担忧之一是由叉子处理组成:诚实地说,我不知道这个复制 – 写入策略已经实施,但我认为任何积极的(或乐观的)策略都可以通过类似于交换的本地策略来缓解。 也就是说,除了克隆(和调整)分叉的进程代码页和调度结构之外,还可以在实际写入之前复制一些其他数据页,在父进程为了更频繁地写入而访问的那些页中进行select(即,使用计数器进行写操作)。
一切,当然,恕我直言。
如果你的内存被进程彻底耗尽到可能威胁系统稳定的程度,那么OOM杀手就会进入画面。 OOM杀手的任务是杀掉进程,直到有足够的内存空间来释放其余进程的平稳运行。 OOM杀手必须select“最好的”过程来杀人。 这里的“最佳”是指这样一个过程,它可以在杀死时释放最大的内存,对系统也是最不重要的。 主要目标是杀死最less数量的进程,最大限度地减less所造成的损害,同时最大限度地释放内存。 为了促进这一点,内核维护每个进程的oom_score。 您可以在pid目录下的/ proc文件系统中看到每个进程的oom_score
# cat /proc/10292/oom_score
任何进程的oom_score值越高,OOM Killer在内存不足情况下被杀的可能性就越高。
信用: – Linux内核正在启动OOM杀手