top 和 ps aux 里那几列 VIRT / RES 你大概天天看,但真被问到”这个进程到底占了多少内存”时,很多人会愣一下。其实答案根本不唯一——取决于你想回答什么问题。Linux 为进程内存提供了 VSZ、RSS、PSS 三套尺子,每把尺子回答的是不同的”内存问题”。
这篇把三者拆开讲清楚:各自的准确定义、哪一步会重复计算共享内存,以及在实际排查里到底该用哪把尺子。
VSZ:进程”有权访问”的地址空间
VSZ(Virtual Memory Size,虚拟内存大小)指进程映射的虚拟地址空间总大小,单位通常是 KB。
它把以下所有东西都算进去:
- 代码段、数据段、堆、栈
- 共享库的映射
- 内存映射文件(mmap)
- 尚未真正占用物理内存的部分——例如换出到磁盘的页、按需分配(demand paging)还没触达的页
有个关键认知要建立:VSZ 反映的是进程”有权访问”的地址空间,不是它实际占了多少物理内存。这就解释了为什么几乎所有进程的 VSZ 都比真实占用大得多——尤其你开着编辑器、IDE、浏览器的多标签页时,随便一个进程 VSZ 冲到几 GB 都很正常。
还有一点:两个进程共享同一份共享库时,各自的 VSZ 都会完整计入这份库。VSZ 在这个意义上”重复”,但它本来就不在乎物理性——它统计的是地址空间,不是物理页。
RSS:实际待在物理内存里的页面总量
RSS(Resident Set Size,常驻内存集)指进程实际驻留在物理内存中的页面总量。
它包含两部分的混合:
- 进程私有的页
- 与别人共享的页(共享库、共享内存等)
问题就出在第二部分。RSS 会把共享内存重复计算。举一个经典例子:两个进程都加载同一个 100MB 共享库,物理上这块库只占 100MB。可两个进程的 RSS 各自报 100MB,加起来虚高到了 200MB。
所以一个常被踩的坑是:把全体进程的 RSS 加起来,通常会明显高估系统的真实内存压力。共享越多的机器(Java/Python/Node 运行时带着大量共享 glibc、共享缓存),这个误差越大。
PSS:按共享者数均摊后的”诚实账本”
PSS(Proportional Set Size,比例式内存集)的设计初衷正是修正 RSS 的重复计数。它把共享内存按共享进程的数量均摊后,再计入每个进程:
PSS = 独占内存 + 共享内存 / 共享者数量
还用前面的例子:两个进程共享 100MB 库,各自 PSS 只计 50MB,两者之和恰好等于真实的 100MB 物理占用。
结论很漂亮:全体进程 PSS 之和,最接近系统真实的物理内存用量。这正是分析整机内存占用时的推荐指标。
需要注意的是,PSS 的精确统计依赖内核开关(如 CONFIG_PROC_PAGE_MONITOR),数据来源主要是 /proc/<pid>/smaps、smaps_rollup,以及 smem 工具。top 里也能通过配置显示 PSS(不同发行版列名略有差异)。
三张尺子横向对比
| 指标 | 含义 | 共享内存是否重复计 | 典型用途 |
|---|---|---|---|
| VSZ | 虚拟地址空间 | 重复(但不计物理性) | 看进程规模 / 是否接近 32 位地址上限 / 虚拟泄漏趋势 |
| RSS | 实际常驻物理内存 | 重复计算 | 粗略看单进程占用、物理泄漏、top 排序 |
| PSS | 按比例分摊后的物理内存 | 不重复 | 汇总整机真实内存占用、容器计费、多进程公平比较 |
查看命令速查:
top:VIRT(VSZ)、RES(RSS);有 SWAP 时也会展示ps aux:列为 VSZ / RSSsmem:默认按 PSS 组织输出cat /proc/<pid>/smaps:精细到每个内存映射段的 PSScat /proc/<pid>/smaps_rollup:整进程汇总,同样带 PSS 段
三把尺子各自的实战场景
光知道定义不够,实际排查里”该用哪个”按问题类型分得很清楚。
VSZ:这个进程”胃口”有多大?会不会撑爆地址空间?
看 VSZ 主要用于回答关于虚拟地址空间的问题:
| 场景 | 用法 |
|---|---|
| 识别进程位数 | 32 位进程 VSZ 不可能超过 malloc / mmap 会失败 |
| 虚拟/地址空间泄漏 | VSZ 持续暴涨而 RSS 不变 → 进程只分配虚拟地址但没真正使用。典型元凶:malloc 后不 touch、线程栈反复创建不回收。这类泄漏不直接吃物理内存,但会让进程最终因地址空间耗尽而崩 |
| 确认预留是否存在 | ulimit -v 限制、JVM 的 -Xmx 只影响虚拟预留。排查”按道理分配了 2GB 为什么 RSS 只有 200MB”时,先看 VSZ 确认预留到底有没有发生 |
| 监控大对象 / 大映射 | mmap 一个 100GB 文件时 VSZ 立刻 +100GB,但 RSS 只有实际读过的页。快速判断代码是否在”假装用了很多内存” |
典型工具:ps aux、top(VIRT 列)、/proc/<pid>/status 的 VmSize。
RSS:这个进程现在真正占了多少物理内存?
单看一个进程、随手查一下占用,RSS 是最顺手的:
| 场景 | 用法 |
|---|---|
| 快速找”谁在吃内存” | top 默认按 MEM%(RSS)排序,抓单进程大致占用、找内存峰值的首选 |
| 单机容量规划 | 评估一个程序跑起来需要几 GB(如数据库 buffer pool、Java 堆 + 元空间),RSS 近似够用 |
| 物理内存泄漏排查 | 物理内存泄漏必然表现为 RSS 持续上涨——这是判断”要不要重启 / 加机器”最直接的信号 |
| OOM Killer 预判 | oom_score 主要看进程 RSS 占比(/proc/<pid>/oom_score),RSS 大者优先被杀 |
典型工具:top(RES 列)、ps aux。
⚠️ 局限要牢记:只看单进程没问题;但把全体进程 RSS 加起来会因共享库虚高很多,同一份 libc / 共享内存被各进程重复计。这一步必须换 PSS。
PSS:整机物理内存到底被谁吃了?
当视角从”单进程”切换到”整机/多进程”时,才轮到 PSS 出场:
| 场景 | 用法 |
|---|---|
| 全机内存账本 | ΣPSS(全部进程)≈ 真实物理内存占用,误差远小于 ΣRSS。排查”free 显示只剩 2GB,但 top 里 RSS 加起来有 8GB”的困惑场景 |
| 容器 / 云环境成本分摊 | 一个容器用了 500MB 共享库(如 Node/Python 运行时的 glibc、共享缓存),按共享者数分摊后的 PSS,才是它”真实消耗的物理资源”。按 RSS 计费会重复收费 |
| 公平比较两个进程 | 进程 A 独享 90MB 大库 vs 进程 B 共享同一个 100MB 库:RSS 看起来差不多甚至 B 更大,PSS 才能反映真实资源消耗差异 |
| 精细化定位 | grep Pss /proc/<pid>/smaps_rollup,定位每个段 / 每份共享库各摊了多少 |
典型工具:smem -k(按 PSS 排序)、cat /proc/<pid>/smaps、/proc/<pid>/smaps_rollup。
实际排查中的一条经验法则
判断”内存泄漏”时到底该看哪个?这里有个简单有效的分流:
- RSS 持续上涨 = 物理内存泄漏。这会实实在在挤压别人,严重时要处理,因为它直接吃物理内存。
- 只有 VSZ 在涨、RSS 不动 = 虚拟/地址空间泄漏(不断预留地址但不落地)。也需要修,但不会立刻吃掉物理内存、不会立刻触发 OOM。
而统计”整机内存到底被谁吃了”时,用 PSS 之和——因为 RSS 之和会因共享库被严重虚增,算完的账是对不上的。
一句话总结怎么选
- 单进程、随手查 → RSS(
top默认就是它) - 地址空间 / 虚拟泄漏 / 位数问题 → VSZ
- 整机内存统计、容器计费、多进程公平比较 → PSS
- 判断泄漏方向 → RSS 涨 = 物理泄漏(严重);只有 VSZ 涨 = 虚拟/地址泄漏(要修,但不立刻吃内存)
下次再有人问你”这个进程占了多少内存”,先反问一句:你要答的是物理占用、地址空间,还是整机账本?