导读:本期聚焦于宋承宪创作的《Linux OOM Killer为什么会触发?如何排查与处理?》,敬请观看详情。内存即将耗尽时,Linux 内核会启动 OOM Killer 强制结束进程来保护系统,但不少管理员直到服务被杀死才注意到问题。本文从内存分配路径出发,解释 OOM Killer 的触发条件,包括 cgroup 限制、内存碎片、overcommit 策略和 swappiness 的影响,并分析内核依据 oom_score 选择进程的规则。随后介绍如何通过 dmesg、/proc/meminfo、/proc/vmstat、/proc/pressure/memory 以及 cgroup 事件定位被杀进程,同时给出查看高风险进程的脚本示例。处理部分涵盖调整 oom_score_adj 保护关键服务、使用 systemd 或 cgroup 设置内存上限、合理配置 overcommit_memory 与 panic_on_oom 等具体方法。文中包含可复用的排查命令与配置示例,并强调监控 MemAvailable 与内存压力指标的重要性,帮助读者提前告警、减少误杀。

Linux 内核设计了一套内存保护机制,当系统内存资源几乎耗尽、无法通过回收或换出满足新的内存请求时,会强制终止一个或多个用户进程,这个机制被称为 OOM Killer。很多情况下,服务进程突然消失、内核日志出现 Out of memory 字样,就是它触发的表现。理解其触发原因,需要从内存分配路径、内存回收和内核评分逻辑三个角度入手。

Linux OOM Killer为什么会触发?如何排查与处理?

一、OOM Killer 的触发条件与进程选择逻辑

OOM Killer 并不会在 free 命令显示内存为 0 时才触发。内核在分配内存时会经过一条比较复杂的路径:先尝试从空闲列表分配,如果失败则进入慢速路径,也就是内存回收。回收动作包括清理页缓存、回收 slab、把匿名页写入 swap 等。只有当这些回收手段仍然无法释放出足够的物理内存,并且系统没有配置 cgroup 级硬限制时,全局 OOM Killer 才会启动。因此,触发 OOM 的本质不是物理内存完全用光,而是内存压力已经超过内核可承受的阈值。

另一个容易忽略的触发来源是内存碎片。当内核需要分配高阶连续页面,比如创建大页、某些驱动需要连续 DMA 缓冲区时,即使系统有较多零散空闲内存,只要无法满足连续的物理页需求,也可能出现分配失败。在默认的 vm.overcommit_memory 策略下,malloc 经常返回成功,但真正写入内存时才会触发 OOM,这也是为什么一些应用在启动阶段一切正常,运行一段时间后才被杀。

内核挑选被终止进程的依据是 oom_score。这个分数由进程当前占用的内存量、是否消耗了大量页表、运行时长、进程优先级等因素综合计算。分数越高,越容易被选中。用户态可以通过 /proc/PID/oom_score_adj 手动调整这个分数,范围从 -1000 到 1000,-1000 表示完全不会被 OOM Killer 选中。如果进程运行在某个 cgroup 内,并且该 cgroup 设置了内存上限,那么触发的是局部 OOM,只会从该 cgroup 的进程中选择,不会影响宿主机上的其他进程。

下面命令可以查看内核全局 OOM 相关参数,帮助确认触发原因:

cat /proc/sys/vm/overcommit_memory
cat /proc/sys/vm/panic_on_oom
cat /proc/sys/vm/swappiness
cat /proc/buddyinfo

其中 overcommit_memory 的值 0 表示启发式判断,1 表示总是允许超额分配,2 表示严格限制,超过可用内存和 swap 总量时直接返回失败。如果系统频繁 OOM,检查这个值为 0 或 1 可能会让问题更隐蔽。

二、通过内核日志和运行时文件定位被杀进程

OOM Killer 触发后,内核会打印详细日志。在较新的内核中,通常可以从 dmesg 或 journalctl 中搜索 killed process 关键字。日志里会包括被杀进程的名字、PID、当时的 oom_score_adj,以及内存使用统计。例如 dmesg -T 输出中会出现类似 Out of memory: Killed process 3301 (java) total-vm:1234567kB, anon-rss:890123kB 这样的信息。看到进程名后,可以结合业务日志判断被杀时该进程在执行什么任务。

除了内核日志,运行时文件也能提供很多线索。/proc/meminfo 中的 CommitLimit 和 Committed_AS 可以判断当前内存承诺是否已经接近上限;/proc/vmstat 中的 oom_kill 字段记录了全局 OOM 发生次数;/proc/pressure/memory 给出内存压力指标,适合持续监控。对于怀疑经常触发 OOM 的系统,可以执行下面的命令快速查看历史事件:

dmesg -T | grep -i -E 'killed process|out of memory'
journalctl -k --since "1 hour ago" | grep -i oom
grep -i 'oom\|killed' /var/log/messages

定位到具体进程后,可以通过 /proc/PID/oom_score 查看当前危险程度。数值越高的进程越容易被杀。oom_score_adj 则是用户可调的偏移量,默认值通常是 0。以下命令可以找出当前系统中 oom_score 最高的前几个进程:

for pid in $(ls /proc | grep -E '^[0-9]+$'); do
  if [ -r /proc/$pid/oom_score ]; then
    score=$(cat /proc/$pid/oom_score 2>/dev/null)
    comm=$(cat /proc/$pid/comm 2>/dev/null)
    echo "$score $pid $comm"
  fi
done | sort -nr | head -10

这个脚本会遍历所有 PID,读取 oom_score 并按从大到小排序。它可以帮助管理员在 OOM 真正发生前识别高风险进程,而不是等到服务中断后再从日志里找原因。

三、处理思路与预防 OOM 的配置实践

处理 OOM 问题通常分两步:先止损,再预防。止损阶段,如果确认某个关键业务进程不能被误杀,可以临时调低它的 oom_score_adj。例如对数据库进程执行 echo -1000 > /proc/$(pidof mysqld)/oom_score_adj,可以让内核在全局 OOM 时优先选择其他进程。但要注意,这样做只是保护单个进程,如果系统内存压力持续存在,内核仍然会杀其他进程,甚至可能导致系统无响应。更稳妥的做法是同时重启占用内存异常的应用,并尽快增加物理内存或启用 swap。

预防阶段可以从内核参数和进程级限制两方面入手。对于容易突然申请大量内存的程序,建议使用 cgroup v2 或 systemd 的内存限制,避免单个进程拖垮整个系统。以 systemd 服务为例,可以在服务单元文件中加入 MemoryMax 和 MemoryHigh,限制内存峰值。例如:

[Service]
ExecStart=/usr/bin/java -jar app.jar
MemoryMax=2G
MemoryHigh=1.6G

MemoryHigh 是软限制,当内存超过该值时会触发更积极的回收;MemoryMax 是硬限制,超过后 cgroup 内会触发局部 OOM。这样即使某个应用内存泄漏,最坏情况也只是这个服务被杀,不会影响同一台服务器上的其他服务。

内核参数方面,如果业务程序会主动检查内存分配结果,可以将 vm.overcommit_memory 设置为 2,并调整 vm.overcommit_ratio。这样内核会在内存承诺超限时直接让 malloc 返回失败,而不是等真正使用内存时触发 OOM。如果系统容易因为内存压力导致死锁,可以将 vm.panic_on_oom 临时设置为 1,让系统在 OOM 时直接重启,这对于无人值守的服务器有时比卡死更可控。长期来看,合理配置 swap 大小和 vm.swappiness 也很重要。对于大部分 Linux 服务器,将 swappiness 设为 10 到 30 可以在内存紧张时优先回收页缓存,减少不必要的 swap I/O。最终,如果业务负载确实需要更多内存,增加物理内存才是最根本的解决办法。

监控同样不可忽视。建议持续收集 /proc/meminfo 中的 MemAvailable、/proc/pressure/memory 以及 cgroup 的 memory.events,当内存压力在几分钟内持续升高时提前告警。结合日志中的 oom_kill 次数,可以判断当前配置下的内存容量是否满足业务需求,避免反复出现 OOM Killer 误杀关键进程的情况。

OOM Killer内存回收Linux内核修改时间:2026-08-27 00:15:39

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。