当系统内存紧张,或者应用出现疑似内存泄漏时,仅靠 top 或 free 提供的粗略数字往往不够。smem 和 pmap 是两个轻量级工具,能帮你更精细地解剖进程的实际内存占用。
smem — 看清“按比例分摊”后的真实内存
传统工具(如 ps)报告的 RSS(常驻内存集)会把共享库(如 libc)的内存重复计入每一个使用它的进程,导致多个进程的内存加起来远大于物理内存,容易引起误判。smem 默认显示 PSS(按比例分摊集),它将共享内存页按使用进程数均分,让你看到每个进程真正“摊派”到它身上的内存量。
常见用法:
# 查看所有进程,按 PSS 降序排列
smem -r -s pss
# 查看特定用户(如 www-data)的内存占用汇总
smem -u www-data
# 按进程分组,显示总 PSS 和共享内存
smem -p
# 生成饼图(需要 python-matplotlib)
smem --pie name
实用场景:
- 快速定位“内存大户”,尤其是大量子进程共享同一库的场景(如 Apache prefork 模式),RSS 可能会产生误导,而 PSS 更接近真实开销。
- 评估容器或 cgroup 内整体内存占用,
smem支持按用户、进程名甚至映射文件来汇总。
示例输出解读:
PID User Command Swap USS PSS RSS
1234 root /usr/bin/java 0 512.0M 523.5M 580.2M
5678 mysql /usr/sbin/mysqld 0 256.3M 270.1M 340.8M
- USS:进程独自占用的非共享内存(唯一集合)。杀掉进程可释放的全部内存。
- PSS:独自占用 + 均摊的共享部分,最常用于跨进程汇总比较。
- RSS:常驻内存,包含完全共享部分,数值会偏大不适用于跨进程求和。
pmap — 查看单个进程的内存地图
pmap 能展开进程的虚拟内存布局,列出其中每一个映射区域的具体大小、权限和映射文件。对于定位内存泄漏、大块匿名内存或文件映射异常十分有效。
基本用法:
# 显示进程 1234 的内存映射详情
pmap -x 1234
# 只显示设备号和映射文件,便于追踪打开的文件
pmap -d 1234
# 显示扩展格式(包含脏页等)
pmap -X 1234
pmap -x 输出各列含义:
- Address:虚拟内存起始地址
- Kbytes:区域大小(KB)
- RSS:常驻物理内存(KB)
- Dirty:脏页大小(需要写回或释放时写回的页)
- Mode:权限(r=读, w=写, x=执行, s=共享, p=私有)
- Mapping:映射的文件路径,或
[ anon ]表示未关联文件的匿名内存(如堆、栈、mmap 分配的内存)
实用场景:
- 进程 RSS 很大,但不知道是哪些部分造成的。通过
pmap -x PID可以快速看到是某个大文件被映射进内存,还是存在大量匿名内存(可能是应用内存泄漏)。 - 分析 JVM 或数据库进程的内存结构:你会发现堆外内存往往以匿名区域的形式出现,通过
pmap能直观感知其规模。 - 快速判断共享库加载情况,查看
[ anon ]内存是否异常膨胀。
典型排错流程:
- 取得目标进程 PID。
- 运行
pmap -x PID | sort -k3 -n -r | head -20,按 RSS 降序列出占用最大的前 20 块内存区域。 - 观察 Mapping 列:
- 大量小块的
[ anon ]:可能是malloc碎片或内存泄漏; - 单个超大
[ anon ]:可能是堆或定制 mmap; - 大文件映射:检查该文件是否应持续驻留内存。
- 结合
strace、/proc/PID/smaps等进一步求证。
总结对比
| 工具 | 擅长维度 | 典型命令 |
|------|---------|---------|
| smem | 多进程横向比较、按PSS看清共享分摊后的真实占用 | smem -r -s pss |
| pmap | 单进程纵向解剖、展现每个内存区域的细节 | pmap -x PID |
两个工具可以相互配合:先用 smem 找出异常消耗内存的进程,再用 pmap 深入分析该进程内部的内存分布,往往能迅速逼近问题根源。