定位思路
应用内存泄漏定位的实用方法是差分:对目标进程的 smaps_rollup 按固定间隔连续采样(至少三次,并跨越一个完整业务周期),看哪类字段在单调增长——Anonymous 与 Private_Dirty 持续增长指向应用分配泄漏,file 类增长多为映射缓存。确认增长后,用 pmap -x 对比具体内存段缩小范围,最后交给应用侧工具(堆分析、GC 日志)才能定位到代码。
基本信息
| 采样接口 | /proc/<pid>/smaps_rollup(读取开销小) |
|---|---|
| 采样建议 | 间隔 1-5 分钟,连续 3 次以上并跨过完整业务周期 |
| 判读口径 | Anonymous/Private_Dirty 增长多为分配;file 类多为缓存 |
| 下钻工具 | pmap -x、/proc/<pid>/maps |
| 容器视角 | cgroup memory.stat 的 anon/file/shmem 构成 |
| 应用侧互证 | 堆指标、GC 日志、对方语言的堆外内存统计 |
典型使用场景
- 容器内存缓慢上涨并触发限额告警,需要确认是进程泄漏还是缓存
- 长稳测试的内存回归验证:发布前后用同一套采样流程对比
- 托管语言应用判断增长发生在堆内还是堆外
- 区分正常缓存增长与真实泄漏,避免误报工单
- 为应用团队提供可复现的采样数据与时间线
采样与下钻
采样与下钻
for i in 1 2 3; do date; grep -E '^(Rss|Pss|Anonymous|Private_Dirty)' /proc/12345/smaps_rollup; sleep 60; done # 连续采样
sudo pmap -x 12345 | sort -k3 -rn | head # 按 RSS 排序找到最大的内存段
cat /proc/12345/maps | grep -E 'heap|anon' | head # 查看可疑映射区间
grep -E '^(anon|file|shmem) ' /sys/fs/cgroup/myapp.slice/memory.stat # 容器视角的构成
cat /proc/12345/status | grep -E 'VmRSS|VmSwap' # 与 status 口径交叉核对
pidstat -r -p 12345 1 3 # 看缺页速率,判断增长是否伴随新分配增长信号判读
| 观察对象 | 增长含义 | 下一步 |
|---|---|---|
| Anonymous / Private_Dirty | 应用自身的分配 | 高度怀疑泄漏,转应用侧堆分析 |
| Rss | 总常驻内存 | 需结合构成判断来源 |
| file 类映射 | 文件页缓存 | 通常不算泄漏,可在压力下被回收 |
| Swap | 已换出数据量 | 增多说明物理内存吃紧而非泄漏本身 |
必须先排除缓存与 GC 波动:托管语言的堆会随回收周期起落,采样窗口要覆盖完整周期。只看相隔很短的两次读数,很容易把正常的周期性波动当成泄漏。
托管语言的泄漏可能发生在堆外(JNI、直接内存、映射文件)。内核侧看到持续增长而堆指标平稳时,方向应转向堆外分配,而不是继续怀疑垃圾回收配置。