Linux的OOM终结者

现在是早晨6点钟。已经醒来的我正在总结到底是什么事情使得我的起床闹铃提前了这么多。故事刚开始的时候,手机铃声恰好停止。又困又烦躁的我看了下手机,看看是不是我自己疯了把闹钟调得这么早,居然是早晨5点。然而不是,而是我们的监控系统显示,Plumbr服务出故障了。

作为这个领域的经验丰富的老鸟,我打开了咖啡机,这是正确解决问题的第一步。一杯咖啡在手之后,现在我可以开始处理故障了。首先要怀疑的是应用程序本身,因为它在崩溃之前一点异常也没有。应用程序日志中没有错误,没有警告,也没有任何可疑的信息。

我们部署的监控系统发现进程已经挂掉了并重启了服务。由于现在咖啡因已经流淌在我的血液中了,我开始变得信心十足。果然在30分钟后,我在/var/log/kern.log日志中发现了下面的信息:




Jun 4 07:41:59 plumbr kernel: [70667120.897649] Out of memory: Kill process 29957 (java) score 366 or sacrifice child
Jun 4 07:41:59 plumbr kernel: [70667120.897701] Killed process 29957 (java) total-vm:2532680kB, anon-rss:1416508kB, filers:0kB



很明显我们被Linux内核给坑了。你知道的,Linux里面有许多邪恶的怪物(也叫作守护进程)。这些守护进程是由几个内核作业所看管的,其中的一个犹为恶毒。所有的现代Linux内核中都会有一个内存不足终结者(Out of memory Killer, OOM Killer)的内建机制,在内存过低的情况下,它会杀掉你的进程。当探测到这一情况时,这个终结者会被激活,然后挑选出一个进程去终结掉。选择目标进程使用的是一套启发式算法,它会计算所有进程的分数,然后选出那个分数最低的进程。

[b]理解”Out of memory killer“[/b]

默认情况下,Linux内核会允许进程请求的内存超出实际可用内存的大小。这在现实世界中是有意义的,因为大多数进程其实并不会用到所有分配给它的内存(注:同一时间内不会全用到)。和这个问题最类似的就是运营商了。他们承诺卖给用户的都是100Mb的带宽,这实际上远远超出了他们的网络容量。他们赌的就是用户实际上并不会同时用完分配给他们的下载上限。一个10Gb的连接可以很轻松地承载100个以上的用户,这里的100是通过简单的数学运算得出的(10G/100M)。

这个做法的一个很明显的副作用就是,万一有一个程序正走上了一条耗尽内存的不归路怎么办。这会导致低可用内存的情况,也就是没有内存页能够再分配给进程了。你可能也碰到过这种情况,没有root帐户你是杀不掉这种顽固的进程的。为了解决这一情况,终结者被激活了,并找出了要终结的进程。

关于"Out of memory killer"参数的调整,可以参考下[url=https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/s-memory-captun.html]这篇文章[/url]。

[b]是谁触发了Out of memory killer?[/b]

虽然现在已经知道发生了什么,但还是搞不清楚到底是谁触发了这个终结者,然后在早晨5点钟把我吵醒。进一步的分析后找到了答案:





[*] /proc/sys/vm/overcommit_memory中的配置允许内存的超量使用——该值设置为1,这意味着每个malloc()请求都会成功。

[*] 应用程序运行在一台EC2 m1.small的实例上。EC2的实例默认是禁用了交换分区的。



这两个因素正好又赶上了我们服务的突然的流量高峰,最终导致应用程序为了支持这些额外的用户而不断请求更多的内存。内存超量使用的配置允许这个贪心的进程不停地申请内存,最后会触发这个内存不足的终结者,它就是来履行它的使命的。去杀掉了我们的程序,然后在大半夜把我给叫醒。

[b]示例[/b]

当我把这个情况描述给工程师的时候,有一位工程师觉得很有意思,因此写了个小的测试用例来重现了这个问题。你可以在Linux下编译并运行下面这个代码片段(我是在最新的稳定版Ubuntu上运行的)。




package eu.plumbr.demo;
public class OOM {

public static void main(String[] args){
java.util.List l = new java.util.ArrayList();
for (int i = 10000; i < 100000; i++) {
try {
l.add(new int[100_000_000]);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
}



然后你就会发现同样的一个 Out of memory: Kill process <PID> (java) score <SCORE> or sacrifice child信息。

注意的是,你可能得调整下交换分区以及堆的大小,在我这个测试用例中,我通过-Xm2g设置了2G大小的堆,同时交换内存使用的是如下的配置:




swapoff -a
dd if=/dev/zero of=swapfile bs=1024 count=655360
mkswap swapfile
swapon swapfile



[b]解决方案?[/b]


这种情况有好几种解决方案。在我们这个例子中,我们只是把系统迁移到了一台内存更大的机器上(裤子都脱了就让我看这个?)我也考虑过激活交换分区,不过咨询了工程师之后我想起来JVM上的GC进程在交换分区下的表现并不是很理想,因此这个选项就作罢了。

还有别的一些方法比如OOM killer的调优,或者将负载水平分布到数个小的实例上,又或者减少应用程序的内存占用量。


原创文章转载请注明出处:[url=http://it.deepinmind.com/java/2014/06/12/out-of-memory-kill-process-or-sacrifice-child.html]http://it.deepinmind.com[/url]

[url=https://plumbr.eu/blog/out-of-memory-kill-process-or-sacrifice-child?utm_source=feedly&utm_reader=feedly&utm_medium=rss&utm_campaign=rss]英文原文链接[/url]
<think>嗯,用户想深入了解LinuxOOMkiller的分析工具和解决方案。从用户提问的措辞看,ta应该是遇到实际OOM问题了,需要具体的排查手段而非理论解释。用户引用的资料里包含三个站内引用:-引用1提到触发OOM的实验方法(Python内存耗尽代码)、基础原理-引用2强调内核参数配置和进程优先级调整-引用3详述了工作机制和内存管理背景需要整合这些信息,但重点要落在用户要的“工具”和“解决方案”上。注意到ta上次提问已了解过原理,这次明显是实操需求。用户可能正在经历生产环境OOM,情绪上比较急切。回复需要:①立即可用的诊断命令(如dmesg/syslog筛查)②缓解方案排序:快速止血方法(临时调整score_adj)和根治方案(内存分析)③给出风险提示(如oom_score_adj的副作用)特别要注意引用1末尾的Python测试代码很有用,可以改写成更安全的测试方案(带内存限制的测试脚本)。对于解决方案层次:1.短期:优先级调整+进程保护2.中期:应用内存优化3.长期:架构扩容内存分析工具要区分实时监控(htop)和事后分析(coredump),用户可能更需要前者。引用2提到的/proc参数调整要强调安全范围,避免随意修改oom_kill_allocating_task导致雪崩。</think> 基于Linux OOM Killer的分析工具及解决方案如下,结合系统监控、内核机制和调优策略分步说明: --- ### **一、分析工具(诊断OOM事件)** 1. **`dmesg`日志分析** - 核心工具:OOM Killer触发后会在系统日志中记录详细信息 ```bash dmesg -T | grep -i "killed process" # 或直接查看系统日志 grep -i "oom" /var/log/syslog ``` - 输出示例: ``` [Tue Jun 4 10:23:45 2024] Out of memory: Killed process 1234 (java) [Tue Jun 4 10:23:45 2024] oom-kill:constraint=CONSTRAINT_NONE [...] oom_score=987 ``` 2. **`/proc`文件系统** - **`/proc/<pid>/oom_score`**:进程当前OOM评分(越高越易被杀) - **`/proc/<pid>/oom_score_adj`**:用户可调的评分修正值(范围:-1000~1000) - **实时查看**: ```bash watch -n 1 "cat /proc/*/oom_score | sort -nr | head -n 5" ``` 3. **监控工具** - `htop`/`top`:实时内存占用排名 - `vmstat 1`:监控swap使用、内存回收压力 - `sar -r 1`:历史内存使用趋势(需安装`sysstat`) --- ### **二、解决方案(预防与调优)** #### **1. 优先级调整:保护关键进程** ```bash # 降低关键进程被OOM Killer选中的概率(设为负值) echo -1000 > /proc/<pid>/oom_score_adj ``` - **永久生效**:在启动脚本中添加`echo -1000 > /proc/self/oom_score_adj` #### **2. 内核参数调优** | 参数路径 | 作用 | 推荐值 | |---|---|---| | `/proc/sys/vm/panic_on_oom` | 是否系统崩溃 | `0`(启用OOM Killer) | | `/proc/sys/vm/oom_kill_allocating_task` | 是否杀死申请者 | `0`(评估所有进程) | | `/proc/sys/vm/overcommit_memory` | 内存分配策略 | `2`(禁止过度分配)[^2] | | `/proc/sys/vm/swappiness` | Swap使用倾向 | `10`(避免频繁swap) | #### **3. 应用层优化** - **限制进程内存**: ```bash systemd服务配置:MemoryLimit=2G # cgroup限制 或使用 ulimit -v 2000000 # 虚拟内存上限 ``` - **内存泄漏检测**: - C/C++:`valgrind --leak-check=yes` - Java:`jmap -histo <pid>` + MAT分析堆转储 #### **4. 兜底策略** - **Kdump+Coredump**:崩溃时保存内存快照 ```bash # 启用coredump echo "/tmp/core-%e-%p" > /proc/sys/kernel/core_pattern ulimit -c unlimited ``` - **容器环境**:Kubernetes设置`resources.limits.memory` --- ### **三、特殊场景处理** 1. **OOM未触发但内存耗尽** - 检查`/proc/sys/vm/overcommit_memory=2`,并确保`/proc/sys/vm/overcommit_ratio`合理 - 确认未禁用swap:`free -m`查看swap分区状态 2. **频繁OOM但内存未满** - 可能原因:内核碎片化(`buddyinfo`查看) - 解决方案:启用透明大页(THP)或重启服务释放碎片[^3] --- ### **
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值