排查linux频繁oom的核心是定位持续内存消耗者及原因,需先固定现场:第一步用dmesg或系统日志确认oom事件真实性,对比committed_as与commitlimit;第二步用smem、slabtop和/proc/meminfo分层分析内存压力来源;第三步深入可疑进程的smaps、缺页行为及gc日志;第四步核查overcommit策略与oom_score_adj配置。

排查 Linux 系统频繁 OOM,核心是抓住“谁在持续吃内存”和“为什么吃不完”,而不是只看哪次被杀的进程。重启会清空关键证据,必须先固定现场再分析。
第一步:确认确实是 OOM,不是误判或假警报
很多“内存不足”其实是缓存占位导致的视觉误差。先验证是否真触发了内核 OOM Killer:
- 运行 dmesg | grep -i "out of memory" —— 如果有输出,说明内核确实执行了 kill 行为
- 检查 grep -i oom /var/log/messages(CentOS/RHEL)或 /var/log/syslog(Ubuntu/Debian),确认时间、被杀进程名和 PID
- 对比 free -h 和 cat /proc/meminfo:重点看 Committed_AS 是否接近 CommitLimit;如果 Committed_AS > CommitLimit,说明虚拟内存已超支,OOM 必然发生
第二步:定位真实内存压力来源,别只盯 RSS
RSS 高≠真问题。Linux 内存压力可能来自匿名页、页表、slab 或内核对象。需分层查:
- 用 smem -w -k -c "pid user command pss uss swap" 查 PSS(比例集大小),比 RSS 更准确反映进程实际内存贡献
- 运行 slabtop -o,重点关注 dentry、inode_cache、ext4_inode_cache 是否异常增长(常见于大量小文件操作)
- 查 cat /proc/meminfo | grep -E "(Active\(anon\)|Inactive\(anon\)|SwapCached|PageTables|KernelStack)":若 Active(anon) 持续飙升,说明用户态堆/栈在涨;若 PageTables 过大,可能是进程太多或单进程虚存映射爆炸(如 Java 直接内存未释放)
第三步:深挖可疑进程的内存行为
找到高 PSS 进程后,不能只 kill,要判断它是“受害者”还是“元凶”:
- 看它的虚拟内存分配情况:cat /proc/PID/smaps | awk '/^Size:/ {sum+=$2} END {print sum}' 得到总虚拟大小,再对比 cat /proc/PID/status | grep VmRSS;差值巨大说明存在大量 mmap 但未使用的区域(如 Java DirectByteBuffer、数据库预分配)
- 观察缺页行为:watch -n1 'cat /proc/PID/status | grep -E "(VmRSS|MMU)"',配合 perf record -e page-faults,major-faults -p PID 看是否频繁触发 major fault(说明在疯狂读写新内存页)
- 对 Java 进程,加 -XX:+PrintGCDetails -Xloggc:gc.log,用 gceasy.io 分析 GC 日志;重点看 Full GC 是否频繁、老年代是否不降、DirectMemory 使用是否持续上涨
第四步:检查系统级配置与 overcommit 策略
有些 OOM 是策略性“放任”导致的,不是程序本身错:
- 查 cat /proc/sys/vm/overcommit_memory:值为 1 表示无条件 overcommit,容易在突发申请时直接爆掉;建议生产环境设为 0 或 2,并配好 vm.overcommit_ratio
- 查 cat /proc/sys/vm/oom_kill_allocating_task:若为 0(默认),内核会遍历所有进程选分最高的杀;若为 1,则直接杀当前申请失败的进程——后者更可控,适合容器化场景
- 确认关键服务是否被保护:cat /proc/PID/oom_score_adj,系统进程应设为 -1000;若业务进程 oom_score_adj 被误调高,可能被优先误杀











