人人都会AI编程

6.6 OOM 机制:内存溢出检测与进程查杀策略

更新时间:2026-07-12

当系统物理内存和交换空间全部耗尽,内核无法为新的内存分配请求找到可用页面时,就必须做出一个艰难的决定:主动终止一个或多个进程,以释放内存、维持系统基本运行。这套机制被称为 Out-Of-Memory Killer(OOM Killer)。它不是系统崩溃前的偶然行为,而是 Linux 内存管理子系统中一套有策略、可观测、可干预的应急方案。

1. OOM 触发条件

OOM 触发并非简单的“内存用光了”。内核会综合考虑:

  • 物理内存(RAM)已全部分配或忙于缓存无法快速回收;
  • 交换空间(swap)已满或未配置;
  • 内存分配请求无法通过回收干净页(如释放文件缓存)或换出匿名页得到满足;
  • 分配请求带有 __GFP_NORETRY 之外的重试标志,内核在多次尝试后最终失败。

当内核判定“内存确实不够,且无法安全等待”后,会调用 out_of_memory() 函数,启动 OOM Killer。

2. OOM Killer 的选择策略

OOM Killer 并非随机选择进程,而是通过一个 坏值得分(badness score) 来决定。得分越高的进程,越可能被选中终止。得分计算大致遵循以下规则:

  • 内存占用量:占用内存越大,得分越高。尤其是匿名内存(进程运行时动态分配)比文件映射内存更容易被视为“罪魁祸首”。
  • 进程重要性:系统关键进程(如 PID 1 的 init、内核线程)得分极低,被保护起来。
  • OOM 调节值:管理员可以通过 /proc/PID/oom_score_adj 手动干预。

得分计算的核心思想是:牺牲一个能释放最多内存、对系统影响最小的进程。内核通过 oom_badness() 函数计算,并用 /proc/PID/oom_score 暴露当前得分。分数范围通常从 0 到 1000,但实际计算可能超出,内核会将其裁剪。

用户可以通过 /proc/PID/oom_score_adj 调整进程的 OOM 倾向,范围是 -1000 到 1000:

  • 设为 -1000:进程完全不被 OOM Killer 考虑,通常用于保护核心服务(如 sshd)。
  • 设为 1000:进程总是被优先选择,可用于标记“可牺牲”的后台任务。

实际操作示例:

# 查看某进程的当前 OOM 得分
cat /proc/1234/oom_score

# 将进程标记为完全禁止被 OOM 杀掉
echo -1000 > /proc/1234/oom_score_adj

# 将一个后台计算任务标记为高概率被杀
echo 500 > /proc/5678/oom_score_adj

调整 oom_score_adj 后,内核会在下一次 OOM 选择时自动考虑新的倾向值。

3. OOM 触发后的日志与现场信息

当 OOM Killer 执行后,内核会打印详细的日志到 dmesg/var/log/kern.log(或系统日志),记录包括:

  • 触发 OOM 时的内存总体使用状况(Total RAM、Free、Swap 等);
  • 每个进程的内存占用(RSS、页表等)及其 OOM 得分;
  • 被选中进程的名称、PID 和得分。

管理员可以通过 dmesg | tail -50grep -i oom /var/log/syslog 快速定位。典型日志片段:

Out of memory: Killed process 1234 (java) total-vm:4194304kB, anon-rss:2048000kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:2048kB oom_score_adj:0

这些信息能帮助你分析是哪个进程导致内存耗尽,以及为何 OOM Killer 做出了这一选择。

4. 重要调节项与策略

  • vm.panic_on_oom:通过 /proc/sys/vm/panic_on_oom 或在 /etc/sysctl.conf 中设置。值为 0 时启动 OOM Killer;值为 1 时内核直接 panic(通常用于要求绝对避免数据损坏的场景);值为 2 且结合内存策略时,在某些条件下强制 panic。
  • vm.oom_kill_allocating_task:设置为 1 时,直接杀掉触发 OOM 的进程,而不是通过全系统评分选择。这种策略虽然简单,但可能误杀无辜(比如此进程可能是被其他进程的分配触发的)。
  • 内存 cgroup 的 OOM 控制:在容器环境中,可以通过 memory cgroup 限制组内总内存,当组内超出限制时,仅在该 cgroup 内部触发 OOM,不会影响主机其他进程。这是现代容器平台隔离 OOM 的核心机制。

5. 实用建议

  • 永远不要将 oom_score_adj 设为 -1000 给所有自以为重要的服务。如果所有进程都被保护,内核最终无法选出合适目标,可能触发内核 panic 或导致系统僵死。合理保护少数关键进程(如 sshd),其余进程交由内核决策。
  • 为生产环境服务启用 swap 并监控。swap 不是洪水猛兽,适量 swap 能让内核有时间做内存回收,避免过早触发 OOM。
  • 部署内存监控告警。OOM Killer 是最后的防线,不应作为常规内存管理手段。用 free -hvmstat、Prometheus 等工具提前发现问题。
  • 容器中务必设置内存限制。否则一个容器的内存泄漏会拖垮整个宿主机,OOM Killer 可能在宿主机层面杀死关键进程。

OOM 机制是 Linux 内存管理中“最后一公里”的硬核保护。理解它的选择逻辑和调节方法,能让你在遭遇内存压力时做出更合理的干预,维持系统在极限状态下的基本可用性。