-1000是唯一真正可靠的oom保护值,因其使进程100%不参与oom评选;oom_score不可修改,仅oom_score_adj可调,范围为-1000至+1000;查高危进程需用ps -eo pid,comm,oom_score,oom_score_adj --sort=-oom_score | head -10。

直接结论:靠 oom_score_adj 调整进程“免死金牌”等级,-1000 是唯一真正可靠的保护值;别信 -17、别碰 vm.oom_kill=0 全局禁用,那是在赌系统不崩溃。
怎么看哪个进程正被 OOM Killer 当成头号目标?
关键不是猜,是查实时分数。OOM Killer 每次触发前,都会按 oom_score 排序,这个值直接反映内核眼里的“该杀优先级”:
-
cat /proc/$(pgrep -f "mysqld")/oom_score—— 查 MySQL 当前得分(数值越大越危险) -
ps -eo pid,comm,oom_score,oom_score_adj --sort=-oom_score | head -10—— 一次性看系统里得分最高的 10 个进程 - 注意:
oom_score是只读的动态计算结果,不能改;真正能动手的是它背后的oom_score_adj
为什么必须用 -1000,而不是 -17 或其他负数?
因为内核版本和发行版差异极大,oom_adj(旧接口)的 -17 在现代内核(≥ 4.0)中已失效,且已被标记为废弃。当前统一使用 oom_score_adj,取值范围严格为 -1000 到 +1000:
-
-1000:内核硬编码逻辑,oom_badness()计算时直接跳过该进程,**100% 不参与评选** -
-999到-1:只是降低分数,但若所有进程都设了负值,OOM Killer 仍会挑其中相对最高的那个杀 -
0(默认):完全按内存占用算分,数据库、Java 应用这类吃内存大户几乎必中
怎么让保护在重启后依然生效?别只靠临时 echo
临时写 /proc/PID/oom_score_adj 只对当前 PID 有效,进程重启就失效。生产环境必须走持久化路径:
- 对 systemd 服务(如
redis.service、sshd.service):用systemctl edit创建覆盖配置 - 执行
sudo systemctl edit redis.service,填入:
[Service] OOMScoreAdjust=-1000
- 保存后运行
sudo systemctl daemon-reload && sudo systemctl restart redis - 验证:重启后检查
cat /proc/$(pgrep redis)/oom_score_adj是否稳定为 -1000
哪些操作看似有用,实则危险或无效?
这些常见做法在真实压测或故障场景下容易翻车:
- 改
vm.oom_kill = 0:全局禁用 OOM Killer,但内存真耗尽时系统会卡死或 panic,比杀一个进程更糟 - 只调
vm.overcommit_memory=2:虽能提前拒绝超量 malloc,但很多应用(如 JVM、PostgreSQL)依赖 overcommit,强行设 2 会导致启动失败或频繁 allocation failure - 用 cron 定期 echo -1000:如果进程刚启动、PID 还没稳定就被 cron 漏掉,那一小段窗口期照样可能被杀
- 保护父进程却忽略子进程:比如只设了
java主进程,但 GC 线程或 JIT 编译线程有独立 PID,它们的oom_score_adj仍是 0
真正难的不是设参数,而是确认业务进程的所有相关 PID(包括 fork 出的子进程、线程组 leader)都被覆盖,且配置嵌入到服务生命周期里,而非靠外部轮询补救。











