跳到主要内容

OOM Killer 排查

问题

进程突然被杀,dmesg 显示 Out of memory: Kill process,如何排查和防范?

答案

OOM Killer 机制

Linux 内存不足时,内核通过 OOM Killer 选择"最该被杀"的进程来释放内存。选择依据是 oom_score(0~1000),分数越高越容易被杀。

# 查看 OOM 事件
dmesg -T | grep -i "out of memory"
dmesg -T | grep -i "killed process"

# 典型输出:
# [Tue Jan 15 14:30:22 2024] Out of memory: Killed process 12345 (java)
# total-vm:8388608kB, anon-rss:7340032kB, file-rss:0kB, shmem-rss:0kB

# 查看完整 OOM 日志(包含内存快照)
dmesg -T | grep -B 10 -A 5 "Killed process"

oom_score 影响因素

# 查看进程的 oom_score(越高越容易被杀)
cat /proc/12345/oom_score

# 查看所有进程的 oom_score,按分数排序
for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do
if [ -f "$proc/oom_score" ]; then
echo "$(cat $proc/oom_score) $(cat $proc/comm 2>/dev/null)" 2>/dev/null
fi
done | sort -rn | head -20

保护关键进程

# oom_score_adj 范围:-1000 ~ 1000
# -1000 = 永远不被 OOM Kill
# 0 = 默认
# 1000 = 最优先被杀

# 保护数据库进程
echo -1000 > /proc/$(pidof mysqld)/oom_score_adj

# 通过 systemd 保护(推荐,永久生效)
# /etc/systemd/system/mysqld.service.d/override.conf
# [Service]
# OOMScoreAdjust=-1000
systemctl daemon-reload
systemctl restart mysqld

# 降低不重要进程被保护的优先级
echo 500 > /proc/$(pidof log-collector)/oom_score_adj
不要保护所有进程

如果所有进程都设置 -1000,OOM Killer 无法杀任何进程,内核会直接 panic。只保护最关键的 1~2 个进程(如数据库)。

根因分析:为什么会 OOM

# 1. 查看内存使用情况
free -h
cat /proc/meminfo

# 2. 查看内存占用最高的进程
ps aux --sort=-%mem | head -10

# 3. 分析内存历史趋势(Prometheus/Grafana)
# 确认是突增还是缓慢泄漏

# 4. 常见 OOM 原因:
原因特征解决方案
内存泄漏内存持续缓慢增长修复代码,定期重启
JVM 堆过大Java 进程 RSS 远超预期调整 -Xmx
缓存未限制应用缓存无上限设置缓存最大内存
连接数暴涨每个连接占用内存限制最大连接数
fork 炸弹大量进程产生ulimit 限制进程数
内存超售虚拟机/容器总内存超物理内存合理分配资源

预防措施

# 1. 关闭 overcommit(严格模式)
# 0 = 启发式(默认) 1 = 总是允许 2 = 严格限制
echo 2 > /proc/sys/vm/overcommit_memory
echo 80 > /proc/sys/vm/overcommit_ratio
# 允许分配的内存 = swap + RAM * 80%

# 2. 设置 swap(给 OOM 一个缓冲)
# 但不要设太大,否则会掩盖问题
swapon --show

# 3. 容器环境设置内存限制
# docker run -m 2g --memory-swap 2g app
# K8s: resources.limits.memory: "2Gi"

常见面试问题

Q1: 如何判断 OOM 是内存泄漏还是瞬时峰值?

答案

  • 内存泄漏:监控图中内存呈锯齿状缓慢上升(重启后下降,然后继续上升),RSS 持续增长
  • 瞬时峰值:某个时间点突然飙升,如批处理任务、大查询、流量突增

排查方法:

  1. 看 Grafana 内存趋势图,区分缓慢增长 vs 突增
  2. 看 dmesg 中的 anon-rss 值是否远超预期
  3. 如果是 Java,用 jmap -histo 查看对象分布

相关链接