深色模式
内存泄漏排查
摘要:内存"看起来不够"往往是 page cache 或 slab 的正常占用,未必是泄漏。本文先教你把账算平,再用 PSS、堆直方图、堆 dump 逐级定位真实泄漏点,并给出可安全采集 dump 的操作方式。
适用环境
bash
free -h
cat /proc/meminfo | grep -E 'MemTotal|MemAvailable|Slab|SReclaimable|Committed_AS'
cat /sys/fs/cgroup/memory.max 2>/dev/null || cat /sys/fs/cgroup/memory/memory.limit_in_bytes1
2
3
2
3
排障步骤
第 1 步:先算清内存账
bash
free -h
cat /proc/meminfo | grep -E 'MemTotal|MemFree|Buffers|Cached|Slab|AnonPages'1
2
2
可用内存应看 MemAvailable,不是 MemFree。
buff/cache 高不等于泄漏
Linux 会尽量用空闲内存做页缓存,压力上来时会自动回收。free 里 cached 大是正常现象。
第 2 步:用 PSS 而非 RSS 排名进程
RSS 会把共享库重复计算,多进程服务严重失真;PSS 才是合理分摊值。
bash
smem -rs pss -k | head -20
ps -eo pid,rss,pmem,comm --sort=-rss | head -151
2
2
第 3 步:观察趋势,确认是否真的"只增不减"
bash
for i in 1 2 3 4 5; do
grep VmRSS /proc/<PID>/status
sleep 60
done1
2
3
4
2
3
4
真正的泄漏表现为:连续观察 RSS 单调上升,且 Full GC 后不回落。
第 4 步:区分堆内与堆外
bash
# 进程虚拟内存分布,看哪一段异常膨胀
pmap -x <PID> | sort -k2 -n | tail -20
cat /proc/<PID>/status | grep -E 'VmSize|VmRSS|VmSwap|Threads'1
2
3
2
3
线程数暴涨会导致栈内存(堆外)占用飙升,这类泄漏在堆 dump 里看不到。
第 5 步:堆直方图与堆 dump
bash
jmap -histo:live <PID> | head -30 # 先看类级分布,开销最小
jmap -dump:live,format=b,file=/tmp/heap.hprof <PID>1
2
2
堆 dump 会导致应用停顿
jmap -dump 触发 Full GC 并伴随 STW,大堆可能停顿数十秒甚至更久。必须先摘流,并在磁盘有足够空间(约等于堆大小)的目录执行。
第 6 步:检查内核侧占用
bash
slabtop -o | head -20
cat /proc/meminfo | grep -E 'Slab|SReclaimable|SUnreclaim|KernelStack'1
2
2
SUnreclaim 或 KernelStack 异常高,通常是内核对象泄漏(如大量 socket、dentry 未回收)。
验证
bash
free -h
grep VmRSS /proc/<PID>/status
# 观察一个完整业务周期(含高峰期)后 RSS 是否回到基线1
2
3
2
3
常见坑
容器内存限制看的是宿主机视角工具
容器内 free 常显示宿主机总量,应以 cgroup 的 memory.max(v2)或 memory.limit_in_bytes(v1)为准。
Swap 打开会掩盖问题
开启 swap 后内存压力被延迟暴露,表现为抖动而非 OOM,容易误判。确认 swapon --show 状态。
只抓一次 dump 无法定位
泄漏需要对比:间隔一段时间后抓两次 dump,比较对象增量才有意义。
删除 dump 之外的日志/文件来"腾空间"
诊断证据一旦删除就不可恢复。dump 文件应保留到复盘完成,必要时先归档再清理。