写一个程序,分配 100MB 堆内存,但不释放——这个进程到底占多少内存?
如果你说"100MB",大概率不对。
Linux 里每个进程的内存报告方式有三种:RSS、PSS、USS。它们说的是三件不同的事。
先看一个真实进程的内存报告
在 Linux 上查看任意进程的内存,最常用的是 cat /proc/<pid>/status:
Name: nginx
VmPeak: 156320 kB # 虚拟内存峰值
VmSize: 156288 kB # 虚拟内存大小(映射了,但不一定分配)
VmRSS: 84320 kB # 实际占用的物理内存(Resident Set Size)
VmData: 98224 kB # 数据段大小(堆 + 栈 + 未初始化数据)
VmRSS 这一行就是常说的 RSS。但 RSS 真的等于进程占用的内存吗?
RSS:驻留内存,但含水分
RSS(Resident Set Size)是进程当前占用的物理内存总量。
但问题在于:RSS 把共享库算进去了。
假设你跑了 3 个 Python 进程,每个都加载了同一个 libpython3.so(10MB)。这 10MB 在物理内存里只有一份,但三个进程的 RSS 都把它算了一遍。

三个进程加起来 RSS 150MB,实际只占 130MB。差了 20MB,这就是共享库的"水分"。
PSS:按比例分摊,业界良心
PSS(Proportional Set Size)就是为了解决这个问题。
共享库那 10MB,三个进程平分,每人算 10/3 ≈ 3.33MB。这样:

PSS 总计 ≈ 130MB,和实际物理内存一致。
Linux 上查看 PSS:
# /proc/<pid>/smaps_rollup 里有 Pss 字段
cat /proc/1234/smaps_rollup | grep Pss
输出格式:
Pss: 43256 kB
Pss_Anon: 38912 kB # 匿名映射(堆、栈、mmap 匿名内存)
Pss_Shmem: 2048 kB # 共享内存
Pss_File: 2296 kB # 文件映射(代码段、mmap 文件)
USS:进程独一份,排查内存泄漏用它
USS(Unique Set Size)是进程独占的物理内存,共享库这部分不算。
# 从 smaps 里算出来
awk '/^Pss:/ {pss+=$2} /^Shared/ {shared+=$2} /^Private/{print} END {print "USS = PSS - Shared/进程数"}' /proc/<pid>/smaps
USS 最干净。如果一个进程的 USS 持续增长,基本可以确定是内存泄漏。
三种指标对比

| 指标 |
含义 |
查看方式 |
用途 |
| RSS |
进程"看起来"占多少 |
ps aux RSS 列 |
粗略估算 |
| PSS |
实际分摊后的内存占用 |
/proc/PID/smaps_rollup Pss |
精确计量 |
| USS |
进程私有内存 |
从 smaps 计算 |
查内存泄漏 |
| VmSize |
虚拟地址空间大小 |
/proc/PID/status |
几乎没用 |
做个实验看三者差距
写几行 C 代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
// 分配 50MB 私有内存(堆)
char *p = malloc(50 * 1024 * 1024);
memset(p, 0, 50 * 1024 * 1024);
// mmap 一个 20MB 匿名映射
void *m = mmap(NULL, 20 * 1024 * 1024, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
memset(m, 1, 20 * 1024 * 1024);
printf("PID: %d\n", getpid());
printf("分配完成,查看 /proc/%d/smaps\n", getpid());
getchar(); // 卡住,方便我们查看
free(p);
munmap(m, 20 * 1024 * 1024);
return 0;
}
编译运行:
$ gcc -o mem_test mem_test.c
$ ./mem_test
PID: 12345
分配完成,查看 /proc/12345/smaps
在另一个终端查看:
$ cat /proc/12345/status | grep -E 'Vm(RSS|Size|Peak|Data)'
VmPeak: 72108 kB # 虚拟内存峰值
VmSize: 72108 kB # 虚拟内存
VmRSS: 71748 kB # RSS ≈ 50MB + 20MB = 70MB
VmData: 51484 kB # 数据段(堆 + 匿名 mmap)
$ cat /proc/12345/smaps_rollup | grep -E 'Pss|Rss|Shared|Private'
Rss: 71748 kB
Pss: 71023 kB
Shared_Clean: 724 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 70324 kB
这里 RSS 71748KB ≈ 70MB,PSS 71023KB ≈ 69.3MB。差距不大,因为这个进程没有加载共享库(纯匿名内存,没有分摊)。
现在换成跑 Python 脚本:
import os, time
# 分配一个大列表
data = [0] * (50 * 1024 * 1024) # 50M 个整数
print(f"PID: {os.getpid()}")
time.sleep(3600)
$ python3 test.py &
$ cat /proc/$(pgrep -f test.py)/smaps_rollup | grep -E 'Pss|Rss'
Rss: 204800 kB # RSS ≈ 200MB
Pss: 187654 kB # PSS 小了,因为 libpython3.so 被多个进程分摊
结论
回到开头的问题:分配 100MB 不释放,进程占多少内存?
- VmRSS:约 100MB出头(含 Python 运行时开销)
- PSS:约 90MB左右(如果加载了共享库,减去分摊部分)
- USS:约 95MB左右(Python 解释器本身也占一些私有内存)
RSS 不是答案,PSS 最接近真相,USS 用来查泄漏。