Linux OOM Killer

Linux 的 OOM Killer (Out-Of-Memory Killer)是内核在物理内存 + swap 真正耗尽 时触发的最后一道防线,它会有选择性地杀死一些进程来释放内存,避免整个系统彻底卡死(完全无响应,需要硬重启)。

1. 为什么需要 OOM Killer?

Linux 内核默认采用**内存过量分配(memory overcommit)**策略:

  • 进程申请内存时(malloc、mmap 等),内核通常先答应(返回成功指针)
  • 真正写内存(触发缺页中断)时才分配物理页
  • 这导致虚拟内存总量可以远超物理内存+swap

当绝大多数进程都开始真正使用内存,且总需求 > 物理RAM + swap 时,就触发 OOM。

没有 OOM Killer 的后果 → 系统假死(soft lockup)、大量进程进入 D 状态、watchdog 触发重启、甚至内核 panic。

2. OOM 触发的主要路径(简要)

复制代码
用户进程缺页 → handle_mm_fault()
              → __alloc_pages()
                  → __alloc_pages_slowpath()
                      → __alloc_pages_may_oom()
                          → out_of_memory()   ← 这里进入 OOM 主逻辑

3. 现代内核(2.6.36 之后,尤其是 5.x+)的打分逻辑 ------ 非常简单了

核心函数:oom_badness()(mm/oom_kill.c)

现在的打分公式(极大简化后)基本可以概括为:

badness = (进程当前使用的内存 / 该进程被允许使用的内存上限) × 1000 + oom_score_adj

因素 说明 大致影响
内存占用比例 anon + file-backed + swap 等,占"允许内存上限"的百分比 主导因素(0~1000)
oom_score_adj 用户可手动设置的调整值(-1000 ~ +1000) 直接加到分数上
是否 root 极轻微优惠(现代内核基本忽略或极小) 几乎无影响
运行时间 现代内核基本不考虑了(以前长寿进程会减分) 已移除
nice 值 基本不影响了 已移除

最关键的两点:

  1. 允许内存上限(allowed memory) 取决于上下文:
    • 全局 OOM → 整个系统的可分配内存
    • cpuset / mempolicy 限制 → 该 cpuset 内的内存
    • memcg(容器)OOM → 该 cgroup 的 memory.max / memory.high
    • 如果有 memcg 限制,则优先在 cgroup 内部杀进程
  2. oom_score_adj 是目前唯一可靠可控的调整手段
oom_score_adj 值 实际效果 典型使用场景
-1000 完全免疫(分数永远 ≤0) 最重要的系统服务(sshd、systemd 等)
-500 大幅降低被杀概率(≈减半内存权重) 重要业务进程
0 默认,无调整 普通进程
+500 大幅提高被杀概率(≈双倍内存权重) 可牺牲的批处理任务
+1000 最高优先级被杀 内存测试、压测脚本

4. 怎么查看和设置?

复制代码
# 查看某个进程的当前分数和调整值
cat /proc/<pid>/oom_score
cat /proc/<pid>/oom_score_adj

# 举例:把 nginx 设为最高保护
sudo sh -c "echo -1000 > /proc/$(pidof nginx)/oom_score_adj"

# 容器常用写法(在容器内执行)
echo -1000 > /proc/self/oom_score_adj

# systemd 服务永久设置(推荐)
# 在 .service 文件中加:
# OOMScoreAdjust=-1000

5. 常见的 sysctl 参数(/etc/sysctl.conf)

参数 推荐值(生产) 说明
vm.panic_on_oom 0 0=正常触发OOM killer,1=部分情况panic,2=强制panic(基本不用)
vm.oom_kill_allocating_task 0 或 1 1=优先杀正在疯狂申请内存的那个进程(很多场景更合理)
vm.overcommit_memory 0 或 1 0=启发式overcommit,1=总是允许,2=严格检查(不推荐生产打开)
vm.overcommit_ratio 50~90 overcommit_memory=0/1 时,overcommit 比例

6. 怎么判断是否被 OOM 杀掉?

  1. dmesg | grep -i "out of memory"
  2. journalctl -k | grep -i "killed process"
  3. /var/log/messages 或 syslog 中搜索 "Out of memory: Killed process"
  4. 进程退出码 137(128+9)通常是 SIGKILL,很可能是 OOM

典型日志样子:

复制代码
Out of memory: Killed process 12345 (java) total-vm:8388608kB, anon-rss:7654321kB, file-rss:0kB, shmem-rss:0kB pgtables:15432kB oom_score_adj:0

7. 如何防止OOM

  1. 设置合理的内存限制
    • 容器 → memory.limit_in_bytes / memory.high
    • systemd → MemoryMax=
    • 物理机 → 尽量别把所有内存都给业务
  2. 关键进程设 -1000
    • sshd、systemd、监控 agent、配置中心等必须 -1000
  3. 可牺牲进程设正值
    • 批处理任务、编译任务、压测进程设 +300 ~ +800
  4. 开启 earlyoom 或 systemd-oomd
    • earlyoom:用户态,提前在内存压力大时杀进程(推荐)
    • systemd-oomd:现代系统自带(需 cgroup v2)
  5. 监控 swap 使用率 + OOM 事件
    • Prometheus + node_exporter + alert rule
相关推荐
hqwest1 小时前
码上通QT实战11--监控页面03-绘制湿度盘和亮度盘
开发语言·qt·绘图·自定义组件·部件·qpainter·温度盘
张心独酌2 小时前
Rust开发案例库-静态服务器
服务器·开发语言·rust
做萤石二次开发的哈哈2 小时前
萤石开放平台 萤石可编程设备 | 设备 Python SDK 使用说明
开发语言·网络·python·php·萤石云·萤石
子有内涵2 小时前
【C++】红黑树实现
开发语言
降临-max2 小时前
JavaWeb企业级开发---Mybatis
java·开发语言·笔记·学习·mybatis
bing.shao2 小时前
golang 做AI任务链的优势和场景
开发语言·人工智能·golang
wifi chicken2 小时前
Linux 内核开发之单链表的增删查改详解
linux·数据结构·链表
我是一只小青蛙8882 小时前
位图与布隆过滤器:高效数据结构解析
开发语言·c++·算法
Object~2 小时前
4.const和iota
开发语言·前端·javascript