进程半夜被 OOM killer 杀掉:从 dmesg 到 cgroup 上限的排查顺序
先确认是不是 OOM
进程被 OOM killer 杀掉时不会留下自己的错误日志,退出码通常是 137(128 + SIGKILL)。先看内核日志:
dmesg -T | grep -i -E "out of memory|oom-kill|killed process"
# [Tue Nov 4 02:13:07 2026] Out of memory: Killed process 18422 (python3) total-vm:4123568kB, anon-rss:3891200kB
journalctl -k --since "2026-11-04 00:00" | grep -i oom
如果 dmesg 已经被刷掉,可以看 /var/log/kern.log 或 journalctl -k --since yesterday。云主机还可能在控制台的「系统事件」里留一条记录。
没有 OOM 记录但进程确实没了,再往别的方向查:systemctl status 看是不是被 systemd 重启、docker inspect 看 OOMKilled 字段、或者程序自己 exit(1)。
内核为什么选它
OOM 日志里有一个 oom_score_adj 和一段任务列表。内核按 oom_score 打分,分最高的先被杀;分数大致和进程占用的物理内存成正比。可以手动调:
cat /proc/18422/oom_score
cat /proc/18422/oom_score_adj
# 让关键进程尽量不被杀(范围 -1000 ~ 1000)
echo -500 > /proc/$(pidof nginx)/oom_score_adj
注意 oom_score_adj 只是降低概率,不是豁免;系统真没内存时该杀还是会杀。
分清是整机内存还是 cgroup 限制
现在大多数服务跑在容器或 systemd scope 里,触发 OOM 的往往不是整机内存,而是这一层 cgroup 的上限:
# 整机
free -h
cat /proc/meminfo | grep -E "MemTotal|MemAvailable|Committed_AS"
# 某个 cgroup v2 路径
cat /sys/fs/cgroup/system.slice/myapp.service/memory.max
cat /sys/fs/cgroup/system.slice/myapp.service/memory.current
cat /sys/fs/cgroup/system.slice/myapp.service/memory.events
memory.events 里的 oom_kill 计数会直接告诉你这一层有没有杀过进程。容器里则是 /sys/fs/cgroup/memory.max(cgroup v2)或 /sys/fs/cgroup/memory/memory.limit_in_bytes(v1)。
整机还有 MemAvailable 却频繁 OOM,基本就是 cgroup 上限太小,或者 vm.overcommit_memory=2 配了过小的 overcommit_ratio。
常见的三个误判
- 看
free的 free 列:Linux 会把空闲内存拿去做 page cache,free很小是正常的,要看available。 - 以为 swap 能兜底:容器里通常没有 swap,云主机也常关;
vm.swappiness调到 0 不等于不用 swap。 - 只盯 RSS:
top的 RES 不含 page cache 和共享内存映射,定位内存大户时smem -r或/proc//smaps_rollup更准。
怎么避免再次发生
# systemd 服务限制内存,并给出明确的重启策略
[Service]
MemoryMax=2G
MemoryHigh=1800M
Restart=on-failure
RestartSec=5s
再配合应用侧的限制:JVM 的 -Xmx、Python 处理大文件时流式读写、Nginx 的 client_max_body_size 和缓冲区上限。最后加监控:memory.current / memory.max 超过 85% 告警,比等 OOM 发生后再查日志省事得多。
一句话:先 dmesg 确认 OOM,再看 oom_score_adj 和 cgroup 的 memory.max,最后才谈调参;只加内存不查泄漏,通常只是把问题推迟几天。
- 点赞
- 收藏
- 关注作者
评论(0)