📋 快速参考 :pmap 用于 报告进程的内存映射关系,是 Linux 系统调试与运维的重要工具。它可以清晰展示进程占用的 虚拟内存区域(VMA),包括每个内存段的起始地址、大小、权限以及对应的文件映射。
📌 目录
🔰 命令简介
pmap(Process Map)是 Linux 下用于 查看进程虚拟地址空间分布 的命令行工具。它通过读取 /proc/<pid>/maps 和 /proc/<pid>/smaps 文件来获取进程的内存映射信息。
💡 核心能力
| 能力 |
说明 |
| 内存布局分析 |
查看进程的代码段、数据段、堆、栈及共享库的映射位置 |
| 内存使用统计 |
统计各内存区域的大小、驻留集大小(RSS)及脏页数量 |
| 匿名内存识别 |
区分文件映射内存与匿名映射内存(标记为 [ anon ]) |
| 权限检查 |
每段内存的读写执行权限一目了然(r/w/x) |
💡 适用场景
| 场景 |
说明 |
| 内存泄漏排查 |
观察进程内存持续增长的区域,定位泄漏来源 |
| 性能调优 |
分析共享库加载情况,优化内存占用 |
| 安全审计 |
检查是否有可写且可执行的内存段(rwx) |
| 故障诊断 |
进程 OOM 或被 kill 时分析内存使用状况 |
⚠️ 本质原理 :pmap 是对 /proc/<pid>/maps 和 /proc/<pid>/smaps 文件的格式化展示工具。也可以直接阅读这些文件获得相同信息。
📖 语法格式
pmap [选项] <PID> [...]
参数说明
| 参数 |
说明 |
PID |
必填 目标进程的进程 ID,可同时指定多个 |
选项 |
可选 控制输出格式和详细程度的参数 |
基本用法
# 查看 PID 为 1234 的进程的内存映射
pmap 1234
# 同时查看多个进程
pmap 1234 5678
# 查看当前 Shell 进程
pmap $$
⚙️ 常用选项
| 选项 |
说明 |
-x, --extended |
显示扩展格式,包含地址、大小、RSS、脏页、权限、映射文件 |
-d, --device |
显示设备号(主设备号:次设备号) |
-q, --quiet |
安静模式,不显示头部和尾部汇总行 |
-A, --range LOW,HIGH |
仅显示指定地址范围内的映射 |
-X |
比 -x 更详细,显示 RSS 和脏页差值信息 |
-XX |
显示内核提供的所有信息(需要较新的内核支持) |
-p, --show-path |
显示映射文件的完整路径 |
-c, --read-rc |
读取默认配置文件 |
-C, --read-rc-from FILE |
从指定文件读取配置 |
-V, --version |
显示版本信息 |
-h, --help |
显示帮助信息 |
⚡ 各发行版注意事项
| 发行版 |
提供包 |
说明 |
| RHEL/CentOS 7/8/9 |
procps-ng |
默认安装,pmap 随 procps-ng 一起提供 |
| Ubuntu/Debian |
procps |
默认安装,无需额外安装 |
| Alpine Linux |
procps |
最小化安装可能不包含,需 apk add procps |
| openSUSE |
procps |
默认安装 |
| Arch Linux |
procps-ng |
默认安装 |
# 检查 pmap 是否可用
which pmap && pmap --version
# Alpine 安装(如未预装)
apk add procps
📊 输出字段说明
默认输出格式
$ pmap 1
1: /usr/lib/systemd/systemd
0000555555554000 1608K r-x-- systemd
00005555558e7000 140K r---- systemd
000055555590a000 52K rw--- systemd
0000555555c8d000 688K rw--- [ anon ]
00007ffff7a00000 1828K r-x-- libc-2.31.so
00007ffff7bc9000 2044K ----- libc-2.31.so
00007ffff7dc8000 16K r---- libc-2.31.so
00007ffff7dcc000 8K rw--- libc-2.31.so
00007ffff7dce000 52K rw--- [ anon ]
00007ffff7fcf000 12K rw--- [ anon ]
00007ffffffde000 132K rw--- [ stack ]
total 32956K
扩展输出(-x)字段详解
$ pmap -x 1
Address Kbytes RSS Dirty Mode Mapping
0000555555554000 1608 1188 0 r-x-- systemd
00005555558e7000 140 140 140 r---- systemd
000055555590a000 52 52 52 rw--- systemd
0000555555c8d000 688 340 340 rw--- [ anon ]
...
total kB 32956 8800 7364
| 字段 |
说明 |
Address |
虚拟内存区域的起始地址(十六进制) |
Kbytes |
该区域的总大小(KB),虚拟内存占用 |
RSS |
常驻内存集大小(KB),实际占用的物理内存 |
Dirty |
脏页大小(KB),被修改但尚未写回磁盘的页面 |
Mode |
内存权限:r=读, w=写, x=执行, s=共享, p=私有 |
Mapping |
映射来源:共享库路径 / [ anon ](匿名内存)/ [ stack ](栈)/ [ heap ](堆) |
匿名内存标记说明
| 标记 |
含义 |
[ anon ] |
匿名映射------没有对应文件的内存区域(如 malloc 分配的内存) |
[ stack ] |
进程的主线程栈 |
[ heap ] |
进程的堆区域(brk() 系统调用扩展) |
[ vvar ] |
内核暴露给用户空间的只读变量页(vDSO 数据区) |
[ vdso ] |
虚拟动态共享对象,加速系统调用 |
[ vsyscall ] |
旧版系统调用加速机制(已被 vDSO 取代) |
🔧 实战示例
1. 查看指定进程的内存映射
# 获取 nginx 的主进程 PID
nginx_pid=$(pgrep -f "nginx: master" | head -1)
# 查看 nginx 主进程的内存布局
pmap $nginx_pid | head -30
2. 扩展格式查看详细信息
# -x 显示 RSS 和脏页
pmap -x 1
# -d 额外显示设备号
pmap -d 1
# 组合使用
pmap -x -d 1
3. 查看特定地址范围
# 仅查看 0x7f0000000000 到 0x7fffffffffff 范围内的映射(通常是共享库区域)
pmap -A 0x7f0000000000,0x7fffffffffff 1234
# 使用扩展模式,只看汇总行
pmap -x $$ | tail -1
# 输出示例:
# total kB 32956 8800 7364
# 解释:虚拟内存 32956KB,物理内存 8800KB,脏页 7364KB
5. 分析内存使用占比
# 查看各映射区域按大小排序
pmap -x $$ | sort -k3 -n -r | head -20
6. 批量检查多个相关进程
# 查看所有 nginx 进程的内存映射
pgrep nginx | xargs -I {} sh -c 'echo "=== PID: {} ==="; pmap -x {} | tail -1'
🔄 与其他工具对比
| 工具 |
定位 |
优势 |
劣势 |
pmap |
进程内存映射报告 |
简洁直观,适合快速查看 |
无法实时更新 |
/proc/<pid>/maps |
原始数据源 |
随时可读,无需安装工具 |
格式不友好,无汇总 |
/proc/<pid>/smaps |
详细内存统计 |
包含 PSS、Swap 等字段 |
数据量大,需手动汇总 |
smem |
内存使用统计 |
支持 PSS/USS,可按用户汇总 |
需要额外安装 |
ps |
进程状态 |
显示总 RSS/VSZ |
无法看到内存分布细节 |
top / htop |
实时监控 |
动态更新,交互式 |
无法显示内存映射详情 |
valgrind massif |
堆分析 |
详细的堆内存使用趋势 |
性能开销大,不适合生产环境 |
不同工具获取信息的对比
# pmap:直观的内存映射报告
pmap -x $$
# ps:总内存占用概览
ps -o pid,rss,vsz,comm -p $$
# /proc/PID/smaps:最详细的信息
cat /proc/$$/smaps | grep -E "^(Pss|Rss|Swap):" | awk '{sum+=$2} END {print "PSS:", sum, "kB"}'
# smem:PSS 统计(需安装)
smem -p -P $$ 2>/dev/null
🎯 高级用法
1. 监控进程内存增长(排查内存泄漏)
#!/bin/bash
# 每 5 秒记录一次进程的 RSS 变化
PID=$1
INTERVAL=5
COUNT=12
echo "时间,RSS(KB),Dirty(KB)"
for i in $(seq 1 $COUNT); do
stats=$(pmap -x $PID 2>/dev/null | tail -1 | awk '{print $3, $4}')
echo "$(date +%H:%M:%S),$stats"
sleep $INTERVAL
done
2. 分析匿名内存占用
# 找出进程中所有匿名内存区域的总大小
pmap -x $$ | grep "\[ anon \]" | awk '{sum+=$2} END {print "匿名内存总计:", sum, "KB"}'
# 找出 RSS 最大的匿名区域
pmap -x $$ | grep "\[ anon \]" | sort -k3 -n -r | head -5
3. 检查可写且可执行的内存区域(安全检查)
# rwx 权限段意味着该内存区域既可写又可执行------存在安全隐患
pmap $$ | grep rwx
# 正常情况下应该没有或极少输出
# 大量 rwx 段可能表明使用了不安全的 JIT 编译或存在安全漏洞
4. 对比进程前后内存变化
# 记录基准状态
pmap -x $$ > /tmp/pmap_before.txt
# 执行某些操作...
# sleep 30
# 记录后续状态
pmap -x $$ > /tmp/pmap_after.txt
# 对比 RSS 总量变化
echo "Before: $(tail -1 /tmp/pmap_before.txt | awk '{print $3}') KB"
echo "After: $(tail -1 /tmp/pmap_after.txt | awk '{print $3}') KB"
5. 生成进程内存分布报告
#!/bin/bash
# 将内存映射按类型分类汇总
PID=${1:-$$}
echo "=== PID: $PID 内存分类汇总 ==="
echo ""
pmap -x $PID 2>/dev/null | awk '
NR>2 && !/^total/ {
if ($5 ~ /\[stack\]/) { stack+=$2; rss_stack+=$3 }
else if ($5 ~ /\[heap\]/) { heap+=$2; rss_heap+=$3 }
else if ($5 ~ /\[anon\]/) { anon+=$2; rss_anon+=$3 }
else if ($5 ~ /\[vdso\]|\[vvar\]/) { vdso+=$2; rss_vdso+=$3 }
else if ($5 ~ /\.so/) { libs+=$2; rss_libs+=$3 }
else if ($5 !~ /\[/) { code+=$2; rss_code+=$3 }
else { other+=$2; rss_other+=$3 }
}
END {
printf "%-20s %10s %10s\n", "类别", "虚拟内存(KB)", "物理内存(KB)"
printf "%-20s %10s %10s\n", "----", "------------", "------------"
printf "%-20s %10d %10d\n", "代码段(可执行文件)", code, rss_code
printf "%-20s %10d %10d\n", "共享库(.so)", libs, rss_libs
printf "%-20s %10d %10d\n", "堆(heap)", heap, rss_heap
printf "%-20s %10d %10d\n", "栈(stack)", stack, rss_stack
printf "%-20s %10d %10d\n", "匿名映射(anon)", anon, rss_anon
printf "%-20s %10d %10d\n", "vDSO/vvar", vdso, rss_vdso
printf "%-20s %10d %10d\n", "其他", other, rss_other
printf "%-20s %10s %10s\n", "----", "------------", "------------"
printf "%-20s %10d %10d\n", "总计", code+libs+heap+stack+anon+vdso+other, rss_code+rss_libs+rss_heap+rss_stack+rss_anon+rss_vdso+rss_other
}'
⚠️ 常见问题
1. 权限不足
# 查看 root 进程会报错
$ pmap 1
1: Operation not permitted
# 解决方案:使用 sudo
sudo pmap 1
⚠️ 注意 :从内核 3.2+ 开始,普通用户只能查看自己拥有的进程的 /proc/<pid>/maps(受 ptrace 访问控制限制)。
2. 进程已退出
# 进程退出后查看
$ pmap 99999
99999: No such process
# 解决方案:先确认进程是否存在
ps -p 99999 > /dev/null 2>&1 && pmap 99999 || echo "进程不存在"
3. 输出过长导致截断
# 对于大型进程(如 Java/数据库),输出可能非常长
pmap -x $(pgrep java) | wc -l
# 建议分页查看或过滤
pmap -x $(pgrep java) | less
pmap -x $(pgrep java) | grep -E "heap|stack|anon"
4. 读取 smaps 很慢
# -XX 选项需要读取 smaps,对于内存大且映射多的进程会很慢
# 例如:一个 Java 进程可能有数千个内存映射
# 替代方案:使用 -x 而非 -XX
time pmap -x $(pgrep java) # 较快
time pmap -XX $(pgrep java) # 可能耗时数秒
📝 自动化脚本
内存泄漏监控脚本
#!/bin/bash
# pmap_mem_watch.sh - 监控进程内存变化趋势
# 用法: ./pmap_mem_watch.sh <PID> [检测次数] [间隔秒数]
PID=$1
CHECKS=${2:-60}
INTERVAL=${3:-10}
if [ -z "$PID" ]; then
echo "用法: $0 <PID> [检测次数] [间隔秒数]"
exit 1
fi
# 检查进程是否存在
if ! kill -0 $PID 2>/dev/null; then
echo "错误: 进程 $PID 不存在"
exit 1
fi
echo "开始监控 PID=$PID,共 $CHECKS 次,间隔 ${INTERVAL}s"
echo "时间 | 虚拟内存(KB) | 物理内存(KB) | 脏页(KB) "
echo "------------------+-------------+-------------+----------"
for i in $(seq 1 $CHECKS); do
if ! kill -0 $PID 2>/dev/null; then
echo "$(date '+%Y-%m-%d %H:%M:%S') | 进程已退出"
break
fi
stats=$(pmap -x $PID 2>/dev/null | tail -1 | awk '{printf "%s | %s | %s", $2, $3, $4}')
printf "%s | %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$stats"
sleep $INTERVAL
done
查找匿名内存占用最大的进程
#!/bin/bash
# 列出当前系统中匿名内存(anon)占用最多的前10个进程
echo "匿名内存 Top 10 进程:"
echo "PID 进程名 匿名内存(KB)"
echo "-------- ------------------ -------------"
for pid_dir in /proc/[0-9]*; do
pid=$(basename $pid_dir)
# 跳过内核线程(comm 为空的情况)
[ -f "$pid_dir/comm" ] || continue
comm=$(tr '\0' ' ' < "$pid_dir/comm" 2>/dev/null)
# 计算匿名内存总量
anon_kb=$(awk '/\[anon\]/ {sum+=$2} END {print sum+0}' "$pid_dir/smaps" 2>/dev/null)
if [ "${anon_kb:-0}" -gt 0 ]; then
printf "%-8s %-18s %s\n" "$pid" "${comm:0:18}" "$anon_kb"
fi
done | sort -k3 -n -r | head -10
📚 总结
pmap 是 Linux 系统中 进程内存分析的利器。它能清晰展示进程的虚拟地址空间分布,帮助运维人员和开发者快速定位内存泄漏、分析内存占用结构、评估进程的内存健康度。相比直接阅读 /proc/<pid>/maps 和 /proc/<pid>/smaps,pmap 提供了更友好的格式化输出和汇总统计。
在日常工作中,pmap 与 ps、top、smem 等工具配合使用,可以构成完整的内存监控体系。对于排查 Java 堆外内存、Node.js Buffer 内存、数据库缓存区等场景,pmap 的 -x 扩展输出尤为实用。