oom killer 在系统物理内存彻底耗尽、无法回收足够页帧时被触发,内核遍历用户态进程,按 badness 分数(rss+swap 占用 × oom_score_adj 归一化系数 ÷ 总可用页数)选择最高者发送 sigkill。

Linux OOM Killer 在系统内存严重不足时被内核触发,目的是终止一个或多个进程以释放内存、防止系统完全僵死。它不是随机选进程杀,而是基于每个进程的 oom_score_adj 值和实际内存占用综合计算“OOM 优先级”,值越高越容易被选中。理解其触发逻辑并合理调优 oom_score_adj,是保障关键服务稳定性的重要运维手段。
OOM Killer 是如何被触发的?
当内核尝试分配内存(如通过 alloc_pages)但无法从 buddy system 或 slab 中获取足够页帧,且所有可回收内存(page cache、slab、swap 等)已基本耗尽时,会进入 OOM 流程。此时内核遍历所有用户态进程(忽略内核线程),为每个进程计算一个 badness 分数:
- 基础分 = 进程 RSS + Swap 使用量(单位:页)
- 再乘以 oom_score_adj 归一化系数(范围 -1000 ~ +1000;-1000 表示永不 kill,0 是默认值,+1000 表示最优先 kill)
- 最后除以总可用内存页数做归一化,得到最终 badness
分数最高的进程被选中并发送 SIGKILL。注意:OOM 并不依赖 swap 是否启用,即使禁用 swap,只要物理内存彻底耗尽,同样会触发。
oom_score_adj 的作用与合法取值
/proc/[pid]/oom_score_adj 是用户可控的接口,用于显式影响 OOM 评分权重。它不是直接设置“分数”,而是调节内核对进程内存“危害性”的判定倾向:
- -1000:进程被标记为 OOM-immune,内核跳过该进程(需 CAP_SYS_RESOURCE 权限)
- 0:默认值,按实际内存使用参与评分
- +500 ~ +1000:适合短命、高内存波动但非关键的进程(如编译任务、临时脚本)
- -500 ~ -999:适合长期运行、低内存但不可中断的关键服务(如数据库主进程、容器 runtime)
注意:oom_score_adj 不改变进程真实内存占用,也不阻止其他机制(如 cgroup memory limit)的限制行为。
生产环境调优建议
盲目设 -1000 存在风险——若关键进程本身内存泄漏,可能拖垮整个系统。推荐分层策略:
-
容器场景:在
docker run或 Pod spec 中使用--oom-score-adj(Docker)或securityContext.oomScoreAdj(Kubernetes),避免手动改 /proc -
systemd 服务:在 service unit 文件中添加
OOMScoreAdjust=-500,比启动后 echo 更可靠 -
监控先行:定期采集
/proc/*/oom_score和/proc/*/status中的MMU、RSS字段,结合dmesg -T | grep -i "killed process"定位被杀原因 - 避免全局设死值:同一类服务在不同负载下内存表现不同,应结合 cgroup v2 memory.max + memory.low 做更精细的资源隔离
常见误区与验证方法
很多人误以为调低 oom_score_adj 就能“保命”,但忽略了根本矛盾在于内存是否真的够用:
- 误区:给 MySQL 设 -1000 后仍被 kill → 实际可能是其子进程(如备份脚本、UDF)未设限,或内核因 page cache 耗尽而误判
-
验证命令:
cat /proc/$(pgrep mysqld)/oom_score_adj查当前值;grep -i "out of memory" /var/log/kern.log看完整 OOM 日志 -
调试技巧:临时用
echo -500 > /proc/$(pidof nginx)/oom_score_adj测试效果,观察后续 OOM 是否转向其他进程
真正健壮的方案,是把 oom_score_adj 当作最后一道保险,而非替代内存容量规划、泄漏排查和 cgroup 配额管理。










