pmap:看进程的内存映射,定位可疑内存段
更新时间:2026-09-19。top/ps 只给一个总 RSS,回答不了「这几个 GB 到底花在堆、栈、mmap 文件还是匿名映射上」。pmap 直接读
/proc/<pid>/maps与smaps,把进程地址空间逐段拆开。JVM 内存结构见堆分代,系统视角见 free 深入。
一、基本命令
bash
pmap <pid> # 列出所有映射段:地址、权限、偏移、设备、映射名
pmap -x <pid> # 增加 RSS/PSS/Dirty/映射页数
pmap -X <pid> # 更完整(来自 smaps_rollup/smaps)
pmap -XX <pid> # 内核提供的全部字段
pmap -x <pid> | sort -k3 -n # 按 RSS 排序找最大段
watch -n 1 'pmap -x 1234 | tail -30' # 连续观察增长bash
Address Kbytes PSS Dirty Mode Mapping
00400000 844 ... r-x-- /usr/bin/app # 程序代码段(只读可执行)
7f...(anon) 1048576 ... rw--- [ anon ] # 大块匿名映射(堆/mmap/JVM heap)
7f...libc.so 2048 ... r-x-- libc.so.6 # 共享库文本段
7ff...stack 132 ... rw--- [ stack ]二、段类型与含义
| 映射 | 含义 | 关注点 |
|---|---|---|
[anon](rw) | 匿名内存:堆、malloc 大块、JVM heap、线程栈 | 持续增长且不回落是泄漏重点 |
[heap] | 主堆(brk 扩展) | glibc 小块分配常在此 |
[stack] / [stack:tid] | 主线程/各线程栈 | 线程数爆炸会产生大量栈段(每线程默认 8M 虚拟) |
| r-x 的 .so | 共享库代码段 | 多进程共享,PSS 而非 RSS 才公平 |
| 文件映射 | mmap 的文件、索引、映射数据 | DB/缓存用,看是不是映射了超大文件 |
三、RSS 与 PSS:共享库别重复算
- RSS:该段占用的物理页(含与其它进程共享的页,全部计入),多进程都链同一个 .so 时会被重复统计,总和会夸大。
- PSS(Proportional Set Size):共享页按进程数平摊,是更公平的占用;判断「这个进程到底独占多少」看 PSS。
- Dirty:被改写、不可丢弃的页;内存紧张时这些页必须换出或触发 OOM,定位泄漏优先看持续增长的匿名 Dirty 段。
四、典型排障
- 内存缓慢上涨:
watch pmap -x对比,若某个[anon]段 Kbytes 随时间线性涨、GC/释放后不降,配合 jmap/valgrind 查泄漏;native 泄漏(堆外)尤其靠 pmap 发现,因为它不在 JVM 堆里。 - 大量相同 .so 或栈段:栈段很多说明线程数失控;结合 jstack 看线程在做什么。
- 超大文件映射:DB/搜索引擎 mmap 整个数据文件时 RSS 巨大,但其中 clean 文件页内存紧张时可回收,不必当泄漏,见free 与缓存。
- 权限异常段:出现大量可写可执行(rwxp)段要警惕 JIT 之外的安全问题。
更细可直接读 /proc/<pid>/smaps(逐段 PSS/Dirty/Swap)和 smaps_rollup 的汇总。
坑
- 只看 RSS 总量就断定泄漏:文件缓存页、共享库会推高 RSS 但可共享/回收,要看匿名 Dirty 段趋势。
- 把虚拟地址空间(虚拟 Kbytes)当物理内存:64 位下线程栈、保留堆动辄很大但未触碰不占物理页,重点看 RSS/PSS。
- 单次快照下结论:内存是否泄漏要多次采样对比是否单调增长。
- 容器内读宿主 pmap 看到的命名空间/文件路径可能不同,注意 PID 映射。
总结
pmap 从 /proc/pid/maps 和 smaps 把进程地址空间拆成代码段、共享库、堆、线程栈和匿名映射,并给出 RSS、PSS、Dirty 等字段;定位内存问题时应多次采样,重点盯住持续增长的匿名可写 Dirty 段以发现堆外/native 泄漏,用 PSS 公平计算共享库占用,同时区分可回收的文件映射页与真正独占的物理内存,再结合 jmap、valgrind 和 /proc/smaps 深入。