OOM Killer 在内存未“用满”时触发,根本原因是内核判定“已无安全可用内存”,即低于 vm.min_free_kbytes 阈值且无法快速回收足够连续页;该阈值是保障内核分配不失败的硬性水位线,误设过高(如1GB+)会导致过早杀进程。

OOM Killer 为什么在内存没“用满”时就开杀?
根本原因不是“物理内存耗尽”,而是内核判定“**已无安全可用内存**”——哪怕 free -h 显示还有几百 MB,只要低于 vm.min_free_kbytes 阈值,且无法通过回收 page cache、swap 等快速腾出足够连续页,就会触发 OOM 流程。
-
min_free_kbytes是硬性水位线,不是建议值;它保障的是内核自身分配内存(如中断上下文、驱动申请)不失败,一旦跌破,内核宁可杀进程也不冒险 - 64 位系统上该值常被误设为 1GB+(如
1024000),远超总内存 1% 的合理范围,导致过早触发 - page cache 占用高(
Cached:字段大)不等于“内存够用”,因为 cache 释放依赖 LRU 和写回延迟,可能来不及满足突发的大块内存申请(如 JVM full GC、数据库 buffer 扩容)
怎么快速确认是不是 OOM Killer 干的?
别只看 dmesg 里有没有 “Out of memory” —— 更可靠的是查 /var/log/messages 或 dmesg -T | grep -i "killed process",匹配到类似这样的行才是铁证:
Killed process 12345 (java) total-vm:8245678kB, anon-rss:3456789kB, file-rss:12345kB
- 必须看到
Killed process+ 进程名 +total-vm/anon-rss数值,这才是 OOM Killer 动手的签名 - 如果只看到
Out of memory: Kill process但没列具体 PID,说明当时连日志缓冲区都快爆了,要立刻检查vm.panic_on_oom是否为1(会直接重启) -
anon-rss值接近你预期内存上限?那基本就是这个进程把系统拖下水了
临时止血和长期配置怎么配?
临时改 min_free_kbytes 可立竿见影,但改错会引发更严重问题;加 swap 是最被低估的低成本方案。
- 临时调低水位(立即生效):
echo 524288 > /proc/sys/vm/min_free_kbytes(对应 512MB,适合 64GB 内存机器) - 永久生效:在
/etc/sysctl.conf加一行vm.min_free_kbytes = 524288,再执行sysctl -p - 强烈建议挂载 swap:阿里云 ECS 默认无 swap,
dd if=/dev/zero of=/swapfile bs=1G count=4 && mkswap /swapfile && swapon /swapfile;虽有性能折损,但能极大延缓 OOM 触发时机 - 禁用 OOM Killer(
vm.oom_kill = 0)是危险操作,仅限诊断用;它会让内存分配阻塞或返回 NULL,应用自己 crash,比 kernel 杀更难排查
如何让关键进程“免死”?
靠改 /proc/,不是靠“优先级高”,而是降低它的“badness 分数”——现代内核公式是:(当前 RSS / 允许上限) × 1000 + oom_score_adj。
- 想保住 Nginx,启动后立刻执行:
echo -1000 > /proc/$(pgrep nginx)/oom_score_adj(-1000 是最低值,表示永不选中) - JVM 进程通常 RSS 波动大,建议在启动脚本里加
-XX:+UseContainerSupport(JDK 10+)并配合 cgroup memory limit,否则oom_score_adj治标不治本 - 注意:容器内修改
/proc/无效,需在宿主机上对容器 init 进程 PID 操作,或通过/oom_score_adj docker run --oom-score-adj -1000启动时指定
真正难处理的从来不是“怎么关掉 OOM Killer”,而是搞清哪个进程在 quietly leak memory —— 它的 anon-rss 持续缓慢上涨,却从不报错,直到某次分配压垮整台机器。这时候 pmap -x 和 /proc/ 才是你的显微镜。










