Linux 内存溢出(OOM,Out of Memory)是指系统或进程申请内存时无法得到满足,内核 OOM Killer 机制被触发并强制终止进程的情形。排查内存溢出需要从内核日志、系统内存状态、进程内存占用、内存泄漏定位四个层面逐步深入,以下是专业完整的排查方法。

一、确认是否发生 OOM(查看内核日志)
Linux 内核在触发 OOM Killer 时会向内核环形缓冲区写入日志,最直接的查看命令是 dmesg:执行 dmesg -T | grep -iE "oom|out of memory|killed process",若出现类似 "Out of memory: Killed process 1234 (nginx) total-vm:…, anon-rss:…" 的记录,即可确认发生过 OOM,日志中包含被杀进程的 PID、名称以及虚拟内存与常驻内存统计。
在 systemd 发行版上可使用 journalctl -k | grep -i oom,或 journalctl -k --since "1 hour ago" | grep -iE "oom|killed" 按时间过滤内核日志;传统系统可检查 /var/log/messages、/var/log/syslog、/var/log/kern.log,执行 grep -i "out of memory" /var/log/messages。此外,cat /proc/vmstat | grep oom 输出中的 oom_kill 计数器可统计系统启动以来被 OOM Killer 杀死的进程总数。
二、查看系统整体内存使用情况
使用 free -h 查看 total/used/free/buff/cache/available,判断物理内存是否耗尽;使用 cat /proc/meminfo 重点关注 MemAvailable、Cached、Slab、CommitLimit 与 Committed_AS,其中 Committed_AS 明显超过 CommitLimit 说明存在内存过度提交(overcommit)。
使用 vmstat 1 持续观察 memory 与 swap 分区,若 si/so(swap in/out)持续非零,说明系统内存压力已大到开始频繁换页;使用 sar -r 1(内存使用率)与 sar -B 1(分页统计)可以采集历史数据,便于事后追溯(需安装 sysstat 包)。
三、定位占用内存最高的进程
使用 top(进入后按 M 按内存排序)或 htop 实时查看;使用 ps aux --sort=-%mem | head -n 10 快速列出内存占用前 10 的进程;也可使用 smem -rs pss 按 PSS(比例分摊集)排序,smem 能区分 RSS、PSS、USS,比 ps 更真实地反映每个进程的实际内存占用。
锁定可疑进程后,查看 /proc/PID/status 中的 VmRSS(当前常驻内存)、VmHWM(历史峰值 RSS)、VmSwap(被换出的内存);使用 pmap -x PID 查看进程各内存映射区域(堆、共享库、匿名 mmap 等)的占用明细,判断是哪个区域异常增长。
四、区分进程级溢出与系统级溢出
排查时要区分两类情形:一是系统内存整体耗尽,表现为 available 极低、OOM Killer 按得分(oom_score)选择高内存占用进程杀死;二是进程自身触碰上限,例如 32 位进程约 3GB 用户态地址空间上限、ulimit -v 设置的虚拟内存限制、cgroup 的 memory.max 限制等。可执行 ulimit -a 查看资源限制;若应用日志出现 "Cannot allocate memory"、"std::bad_alloc"、"java.lang.OutOfMemoryError" 等错误,应结合应用日志与 core dump(提前执行 ulimit -c unlimited 开启,用 gdb 调试)进一步分析。
五、使用工具定位内存泄漏(溢出的根因)
C/C++ 程序推荐 Valgrind:执行 valgrind --leak-check=full --show-leak-kinds=all ./your_program,可输出 Definitely Lost、Indirectly Lost 等分类泄漏详情及调用栈;开发期也可使用 AddressSanitizer(编译时加 -fsanitize=address)以更低开销发现越界与泄漏。
Java 程序使用 jmap -heap PID 查看堆占用概况,使用 jmap -dump:format=b,file=heap.hprof PID 导出堆快照,再用 MAT(Memory Analyzer Tool) 分析 Dominator Tree 与对象保留链定位泄漏对象;配合 jstack PID 查看线程栈、jstat -gcutil PID 1s 观察 GC 是否频繁 Full GC 且回收无效。
线上不便重启的服务,可使用 gcore PID 生成进程核心转储后离线用 gdb 分析;也可使用 perf(如 perf record -e kmem:mm_page_alloc)或 eBPF/bcc 工具集中的 memleak、oomkill 等跟踪工具,对生产环境影响很小。
六、容器与 cgroup 场景的 OOM 排查
容器被杀但宿主机内存充足时,需检查 cgroup 内存限制:cgroup v2 执行 cat /sys/fs/cgroup/memory.max 并查看 memory.events 中的 oom、oom_kill 计数;cgroup v1 执行 cat /sys/fs/cgroup/memory/memory.limit_in_bytes,并关注 memory.failcnt(触限次数)与 memory.max_usage_in_bytes(历史峰值)。Docker 可执行 docker inspect 容器ID,查看 State 中的 OOMKilled 字段是否为 true 以及 HostConfig.Memory 限额;Kubernetes 中 Pod 若因内存超限被杀,状态会显示退出原因为 OOMKilled。
七、内核级内存泄漏排查
若 slabtop 显示 Slab 占用居高不下(如 dentry、inode 缓存过多),先确认是否为可回收缓存(echo 2 > /proc/sys/vm/drop_caches 测试);排除缓存因素后若怀疑内核态泄漏,需要内核编译时开启 kmemleak(CONFIG_DEBUG_KMEMLEAK),通过 echo scan > /sys/kernel/debug/kmemleak 触发扫描并读取该文件输出;必要时配合 ftrace 或 crash 工具分析 vmcore。
八、预防与调优建议
合理配置 vm.overcommit_memory(默认 0 为启发式判断;内存敏感型服务建议设为 2 严格分配,配合 overcommit_ratio 控制提交上限);通过 /proc/PID/oom_score_adj(取值 -1000~1000)保护关键进程,例如将数据库进程设为 -1000 使其不被 OOM Killer 选中;设置 vm.panic_on_oom=0 让内核仅杀死进程而不直接宕机;对业务进程配置 cgroup memory.max 上限,避免单个进程内存失控拖垮整机。
排查思路总结:先用 dmesg / journalctl 确认 OOM 是否发生及被杀进程是谁 → 再用 free、vmstat、sar 判断系统内存压力来源 → 接着用 ps、top、smem、pmap 锁定可疑进程 → 然后用 Valgrind、jmap、gcore、eBPF 等工具定位泄漏代码 → 最后结合 cgroup / 容器限制与内核参数(overcommit、oom_score_adj 等)完成修复与预防。

查看详情

查看详情