首页/系统/Linux 运维命令速查/场景:OOM Killer 杀进程排查

场景:OOM Killer 杀进程排查

服务凌晨被杀、SSH 时断时续:dmesg/journal 里的 OOM 记录会给出被杀进程与内存全景,按证据调整而不是瞎加内存。

Linux 运维命令速查系统更新于 2026-10-13

场景概述

进程毫无征兆地消失、SSH 时断时续、服务重启后一切正常,这类现象里 OOM Killer 是很常见的幕后角色。它在系统内存耗尽时按评分挑一个进程杀掉以保住系统本身,动作会记录在内核日志里,包含当时的内存全景与被选中进程的内存占用。排查的关键是理解两件事:被杀的多半是占用最大的受害者而不是真正的元凶,以及容器环境下的 OOM 可能来自 cgroup 限额而不是宿主机内存不足。

场景信息

触发条件进程被信号终止(退出码 137),或系统出现短时卡顿后自行恢复
排查目标确认是否为 OOM、谁在吃内存,以及触发的是物理内存还是 cgroup 限额
工具链dmesg 与 journalctl -k 找 OOM 记录 → free 与 /proc/meminfo 还原现场 → ps/监控曲线定位增长来源
评分机制内核按 oom_score 选择牺牲对象,主要依据进程占用内存比例,可用 oom_score_adj(单元里对应 OOMScoreAdjust)上下调整

常见成因

  • 应用内存泄漏:RSS 随时间单向增长,最终把机器内存吃满
  • 堆上限设置过大:JVM 等运行时的最大堆接近物理内存,多个实例叠加后必然触发
  • 缓存与队列失控:本地缓存没有上限、任务队列积压把小对象堆成内存黑洞
  • 并发峰值:突发的批量请求让内存占用短时间翻倍,超过实际容量
  • 容器 cgroup 限额:宿主机内存充足,但容器自己的 memory.max 被触及
  • 禁用 swap:在没有换页缓冲的情况下,内存峰值更容易直接触发 OOM

三步排查命令

三步排查命令
# 第 1 步:找 OOM 记录
dmesg -T | grep -i -E 'killed process|out of memory|oom-kill' | tail -n 30
journalctl -k --since '2 days ago' | grep -i oom | tail -n 30
# 第 2 步:还原内存现场
free -h; cat /proc/meminfo | head -n 5
ps aux --sort=-rss | head -n 15
# 第 3 步:确认是否为容器限额触发(记录里含 cgroup 字样)
cat /sys/fs/cgroup/memory.max 2>/dev/null
cat /sys/fs/cgroup/memory.events 2>/dev/null | grep -i oom
docker stats --no-stream | head
# 保护关键进程与限制失控进程(systemd 单元)
# [Service]  OOMScoreAdjust=-500   或   MemoryMax=2G   MemoryHigh=1500M
systemctl show -p MemoryMax -p MemoryHigh myapp.service

OOM 日志读法

片段含义用途
Out of memory: Killed process最终结论:被选中的进程名与 PID确认谁被牺牲
active_anon / inactive_anon匿名内存(堆栈与堆)活跃与不活跃部分判断是应用占用还是缓存
active_file / inactive_file文件页缓存这部分通常可回收,不构成真正压力
slab内核对象占用异常高时怀疑内核对象泄漏或大量小对象缓存
pinned / unreclaimable不可回收内存偏高说明内存被锁定,回收手段有限
被选中进程的 rss 与 total_vm该进程的物理占用与虚拟内存判断它是受害者还是长期占用者
OOM 日志里的进程表会列出所有候选者的内存占用,重点看被杀进程是否长期占据榜首:如果它一直是最大占用者,问题在于它自身的内存管理;如果它只是平均水平的进程,说明是短时间内有人暴涨,需要结合监控曲线找出那个时间点的增长来源。
systemd 单元可以用两个字段做提前量:MemoryHigh 触发回收与限流但尽量不杀,MemoryMax 到达上限即终止;OOMScoreAdjust 调整被杀优先级,给关键服务设负值降低被选中的概率。这些都是缓解手段,真正解决仍要回到内存使用是否合理。
加内存或开大 swap 只是把问题推后:泄漏型增长会继续吃到新上限,而依赖 swap 会明显拖慢延迟敏感的业务。规范做法是先确认增长曲线与触发条件(定时任务、特定请求类型、发布后开始),定位到具体来源后再决定扩容还是改代码。

易错点

  • 把被杀的进程当成元凶:OOM 倾向选择占用最大的进程,它往往只是受害者,要结合时间线与全量候选列表判断
  • 容器里的 OOM 常由 cgroup 限额触发,只查宿主机 free 会得出「内存充足」的错误结论,应看 memory.max 与 memory.events
  • 只看 OOM 记录不看 RSS 增长曲线,无法区分泄漏型增长与一次性峰值,处置手段完全不同
  • 滥用 oom_score_adj 保护进程:把无关进程设成极低优先级会转移风险,而该值也无法保护真正失控的进程
  • 遇到 OOM 就先加内存或开 swap,掩盖了根因,问题在下一次流量增长或发布后再次出现

数据来源与参考

  • 内核文档 admin-guide/mm/oom(OOM Killer)
  • man 5 proc(/proc/meminfo)

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌