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 持续增长
- 瞬时峰值:某个时间点突然飙升,如批处理任务、大查询、流量突增
排查方法:
- 看 Grafana 内存趋势图,区分缓慢增长 vs 突增
- 看 dmesg 中的
anon-rss值是否远超预期 - 如果是 Java,用
jmap -histo查看对象分布