Linux服务器触发OOM Killer怎么办?内存不足、进程被杀与故障排查教程

Linux服务器触发OOM Killer怎么办?内存不足、进程被杀与故障排查教程

Linux服务器上的网站、数据库或容器突然停止,日志中出现 KilledOut of memory 或 oom-kill 时,通常意味着某个进程因内存压力被终止。

很多人遇到这种情况会立即重启服务,或者执行释放缓存命令。这样可能暂时恢复业务,却容易丢失关键现场,也无法判断问题究竟来自整台服务器内存不足、容器内存上限、服务自身限制,还是应用内存持续增长。

正确的排查顺序应该是:先从内核和服务日志确认谁终止了进程,再区分全局OOM、cgroup OOM和 systemd-oomd,随后分析物理内存、Swap、进程RSS、缓存与内存压力,最后根据根因调整程序、服务限制或服务器规格。

本文介绍一套适用于常见Linux服务器的OOM排查流程。不同发行版、内核版本和容器运行时的日志可能不同,生产环境修改限制或重启服务前,应先确认业务和数据安全。

一、OOM Killer是什么

OOM是Out Of Memory的缩写。当内核无法为当前内存分配请求找到足够资源,并且通过回收缓存、回写页面或使用Swap仍无法满足需求时,可能启动OOM处理流程。

为了让系统继续运行,内核会在可终止的任务中选择牺牲对象。选择过程会考虑进程的内存使用情况、可回收资源以及 oom_score_adj 等因素。被选中的进程通常会收到无法捕获的终止信号。

需要注意:

  • OOM不等于物理内存使用率刚好达到100%。
  • 某次大块内存申请失败也可能触发问题。
  • 容器或服务达到cgroup上限时,即使宿主机还有空闲内存,也可能发生cgroup OOM。
  • systemd-oomd可能根据持续内存压力提前终止某个cgroup,不一定等到内核全局OOM。
  • 日志中的 Killed 也可能来自管理员、脚本或服务管理器,必须先核实证据。

二、先确认进程是否真的被OOM终止

查看当前启动周期内的内核日志:

sudo journalctl -k -b | grep -i -E 'out of memory|oom-kill|killed process'

查看最近一小时:

sudo journalctl -k --since "1 hour ago" | grep -i -E 'out of memory|oom-kill|killed process'

也可以检查内核环形缓冲区:

sudo dmesg -T | grep -i -E 'out of memory|oom-kill|killed process'

典型日志可能包含:

Out of memory: Killed process 24831 (java) total-vm:... anon-rss:...

重点记录:

  • 事件发生的准确时间。
  • 被杀进程的PID和名称。
  • anon-rssfile-rssshmem-rss 等内存信息。
  • 日志中是否出现 task_memcgMemory cgroup out of memory 等cgroup字段。
  • OOM发生时的调用进程和约束范围。

如果服务器已经重启,当前 dmesg 可能不包含上一次启动的日志。使用持久化journal时,可以查看上一次启动:

sudo journalctl -k -b -1 | grep -i -E 'out of memory|oom-kill|killed process'

如果系统没有保存持久化日志,应先完善日志配置,否则下一次重启后仍可能缺少证据。

三、区分三种常见的内存终止情况

1. 全局OOM

整台服务器的可用内存和可用Swap不足,内核在全局范围选择进程终止。此时常见现象包括:

  • 多个服务同时变慢。
  • SSH和命令执行卡顿。
  • Swap持续换入换出。
  • 内核日志出现全局内存状态和进程列表。
  • 被杀对象不一定是刚刚申请内存的进程。

2. cgroup OOM

容器、Kubernetes Pod或systemd服务可能受到cgroup内存上限限制。当该组达到限制时,OOM可能只发生在这个cgroup内。

这种情况下,宿主机执行 free -h 可能仍显示较多可用内存,但容器内进程仍会被杀。

常见线索包括:

  • 内核日志出现 Memory cgroup out of memory
  • Docker显示容器因OOM退出。
  • cgroup的 memory.events 中 oom 或 oom_kill 计数增加。
  • 服务或容器配置存在内存上限。

3. systemd-oomd主动终止

部分发行版启用了 systemd-oomd。它会根据cgroup v2提供的内存压力信息和服务策略,提前选择某个cgroup终止,以避免系统进入更严重的内存争用。

检查服务状态和日志:

systemctl status systemd-oomd
sudo journalctl -u systemd-oomd --since "1 hour ago"

如果日志明确显示某个unit被 systemd-oomd 终止,应继续检查该unit的内存压力和systemd内存策略,而不是只搜索内核OOM日志。

四、理解free输出,别把缓存直接当成内存泄漏

执行:

free -h

常见字段包括:

字段常见含义排查重点
total可供系统管理的内存总量确认实例规格
used已使用内存的估算值不宜单独作为告警依据
buff/cache缓冲区和页面缓存很多情况下可以按需回收
available可供新程序使用的估算内存比free字段更有参考价值
swapSwap总量和使用量观察是否持续换入换出

Linux会利用空闲内存缓存文件数据,因此 free 很小并不一定表示内存不足。更应关注 available、Swap活动、内存压力和业务延迟。

只执行一次 free -h 也无法还原OOM发生前的情况。最好通过监控持续记录内存可用量、Swap、进程RSS和cgroup使用量。

五、查看当前内存占用最大的进程

按RSS排序:

ps -eo pid,ppid,user,%mem,rss,vsz,etime,comm,args --sort=-rss | head -n 20

字段说明:

  • RSS:当前驻留在物理内存中的页面总量。
  • VSZ:进程虚拟地址空间大小,不等于真实物理内存占用。
  • %MEM:RSS占系统物理内存的比例。
  • ETIME:进程已经运行的时间。

不要仅根据VSZ判断内存泄漏。Java、数据库和某些运行时可能预留较大的虚拟地址空间,但并未全部占用物理内存。

查看指定进程的汇总信息:

grep -E 'Name|VmPeak|VmSize|VmRSS|VmSwap|Threads' /proc/PID/status

查看更详细的匿名内存、文件映射和共享内存汇总:

cat /proc/PID/smaps_rollup

将 PID 替换为实际进程ID。读取其他用户进程的信息可能需要root权限。

六、查看进程为什么更容易被选中

查看进程当前的OOM评分:

cat /proc/PID/oom_score

查看人为调整值:

cat /proc/PID/oom_score_adj

oom_score_adj 可影响进程被OOM Killer选中的倾向:较高的值通常使进程更容易被选择,较低的值降低被选择概率。

不建议为了保护某个服务,就随意把所有关键进程设置为最低值。这样可能让内核只能终止其他同样重要的服务,甚至使系统更难恢复。

如果需要通过systemd设置,应在充分评估后使用服务级配置,并记录变更原因。例如:

[Service]
OOMScoreAdjust=-500

修改后需要重新加载systemd并重启相应服务才能应用。具体值应结合整机服务优先级设计,而不是照抄固定配置。

七、检查Swap是否缺失或正在抖动

查看Swap:

swapon --show
free -h

持续观察换入换出:

vmstat 1 10

重点关注 si 和 so

  • 偶尔使用少量Swap不一定异常。
  • siso持续较高,通常说明系统正在频繁换页。
  • Swap可以缓冲短时压力,但不能解决长期内存泄漏。
  • 没有Swap的服务器在突发内存申请时可能更快触发OOM。
  • Swap过大也不能替代足够的物理内存,持续换页会显著增加延迟。

是否启用Swap以及设置多大,要根据磁盘性能、业务延迟、数据敏感性和云平台建议决定。数据库等延迟敏感服务还需要结合自身内存管理策略评估。

八、检查容器和cgroup内存限制

Docker容器

查看容器状态:

docker ps -a

检查是否发生OOM:

docker inspect CONTAINER_NAME --format '{{.State.OOMKilled}}'

查看内存限制:

docker inspect CONTAINER_NAME --format '{{.HostConfig.Memory}}'

实时观察:

docker stats

如果容器经常接近上限,应先确认应用实际峰值、缓存策略和并发量,再调整限制。简单取消限制可能让单个容器拖垮整台宿主机。

cgroup v2

在对应cgroup目录中,可以检查:

cat memory.current
cat memory.max
cat memory.high
cat memory.events

其中 memory.events 可显示 highmaxoom 和 oom_kill 等事件计数。具体目录由systemd、容器运行时或编排平台决定,不要假设所有系统路径完全相同。

systemd服务

查看服务限制:

systemctl show SERVICE_NAME -p MemoryCurrent -p MemoryHigh -p MemoryMax -p OOMPolicy

如果 MemoryMax 设置过低,服务可能在整机内存仍充足时发生cgroup OOM。

九、检查是否存在持续内存压力

支持PSI的系统可以查看:

cat /proc/pressure/memory

some 表示至少部分非空闲任务因内存资源争用而停顿;full 表示所有非空闲任务同时因内存压力停顿。持续上升的压力通常比某一时刻的内存使用率更能反映业务是否受到影响。

还可以查看:

vmstat 1 10

如果出现以下组合,需要重点处理:

  • 可用内存持续下降。
  • Swap换入换出持续发生。
  • 内存PSI持续较高。
  • 页面扫描和回收频繁。
  • 网站延迟、数据库等待或系统负载同步升高。

十、常见OOM原因与处理方向

1. 应用内存泄漏

进程RSS随运行时间持续增长,重启后下降但随后再次上涨,可能存在内存泄漏或无界缓存。

处理方向:

  • 使用应用对应的性能分析工具生成内存快照。
  • 检查对象、连接、文件句柄和缓存生命周期。
  • 为队列、缓存和批量处理设置上限。
  • 修复程序,而不是依赖定时重启。

2. 并发或工作进程过多

PHP-FPM、Gunicorn、Java线程池、任务队列和容器副本设置过多,单个进程占用不高,总量却可能超过服务器容量。

应根据单进程峰值内存和可用内存计算合理并发,给操作系统、文件缓存和其他服务保留余量。

3. 数据库缓存设置过大

数据库缓冲池、连接缓冲区和每查询内存叠加后,实际峰值可能远高于一个静态配置值。

调整前应理解配置是全局分配、按连接分配还是按操作分配,并结合最大连接数估算最坏情况。

4. 一次性批处理或大文件操作

导入、导出、压缩、图片处理、日志分析和大文件读取可能在短时间申请大量内存。

可采用分块处理、流式读取、限制并发和错峰执行,避免把整个数据集一次载入内存。

5. 容器限制不合理

限制太低会使应用在正常峰值下被杀;没有限制则可能影响宿主机其他业务。应根据监控数据设置软限制和硬限制,并预留突发空间。

6. 异常流量或恶意请求

高并发、超大请求体、复杂查询或大量连接可能迅速放大应用内存。需要结合访问日志、连接数、请求大小和安全策略判断。

十一、发生OOM后如何安全恢复

1. 先保存现场

在系统仍可操作时保存:

date
free -h
swapon --show
vmstat 1 5
ps -eo pid,ppid,user,%mem,rss,vsz,etime,comm,args --sort=-rss | head -n 30
sudo journalctl -k --since "30 minutes ago"

同时保存应用、容器和数据库日志。

2. 确认服务状态

systemctl status SERVICE_NAME
sudo journalctl -u SERVICE_NAME --since "30 minutes ago"

3. 处理正在失控的任务

如果确认某个非关键批处理仍在快速占用内存,优先通过服务管理器或程序自身机制停止。避免在不了解数据写入状态时直接 kill -9

4. 恢复业务

确认依赖、磁盘空间和配置正常后再启动服务:

sudo systemctl restart SERVICE_NAME

恢复后立即观察RSS、Swap、PSI和业务响应时间,防止进程再次快速增长。

十二、不要用这些方法掩盖问题

1. 不要把清理缓存当成长期方案

页面缓存通常会在应用需要内存时由内核回收。频繁强制清理可能降低文件访问性能,也不能修复应用泄漏。

2. 不要只增加Swap

Swap可能提供缓冲,但无法解决持续增长的内存需求。服务可能从“被杀”变成“长时间卡顿”。

3. 不要保护所有进程

过度降低多个服务的 oom_score_adj 会破坏系统在极端情况下的选择空间。

4. 不要只看当前内存

进程被杀后,内存已经释放。故障后看到大量可用内存并不能证明此前没有OOM。

5. 不要直接认定最大进程就是泄漏源

数据库或Java服务本来就可能是最大内存使用者。应观察增长趋势、配置预算和业务负载。

十三、建立监控避免OOM再次发生

建议持续记录:

  • MemAvailable和Swap使用量。
  • Swap换入、换出速率。
  • 主要进程RSS和增长速度。
  • 容器 memory.current、限制和OOM事件。
  • memory PSI的 some 与 full
  • OOM内核日志和 systemd-oomd 日志。
  • 应用并发、队列长度、缓存条目和数据库连接数。

告警不应只设置为“内存使用率超过90%”。更实用的方式是结合持续时间、可用内存、Swap活动、PSI和进程增长速度,提前发现即将发生的内存压力。

十四、推荐的完整排查顺序

  1. 使用 journalctl -k 和 dmesg 确认是否发生内核OOM。
  2. 查看服务和 systemd-oomd 日志,排除其他终止来源。
  3. 判断是全局OOM、cgroup OOM还是内存压力策略终止。
  4. 记录被杀进程、时间、cgroup和内存字段。
  5. 使用 freevmstat 和PSI判断整机内存压力。
  6. 使用 ps/proc/PID/status 和 smaps_rollup 定位主要内存使用者。
  7. 检查Swap、容器限制、systemd内存限制和 memory.events
  8. 对照应用并发、缓存、数据库配置和批处理任务查找根因。
  9. 保存现场后恢复服务,并持续观察是否再次增长。
  10. 通过程序修复、限制调整、监控或扩容完成长期治理。

总结

Linux服务器出现OOM Killer,不一定只是物理内存容量不足。应用泄漏、并发配置、数据库缓存、批处理任务、容器上限和持续内存压力,都可能导致进程被终止。

排查时首先要找到终止证据,并区分全局OOM、cgroup OOM和 systemd-oomd。随后结合进程RSS、Swap活动、PSI、cgroup事件和应用指标还原内存增长过程。

重启服务只能恢复现场,不能消除根因。长期解决方案应包括合理的内存预算、应用优化、容器限制、压力监控和容量规划,让系统在业务峰值下仍保留足够余量。

知识库

Nginx出现499错误怎么办?客户端断开连接的原因与排查方法

2026-8-17 18:20:27

知识库

网站出现重定向次数过多怎么办?Nginx、HTTPS与CDN循环跳转排查教程

2026-8-18 18:00:54