/proc/[pid]/statm第二列不准,因其统计驻留页总数含共享内存;应读smaps_rollup中Private_Clean与Private_Dirty之和(单位KB)得真正私有RSS。

Linux 下用 /proc/[pid]/statm 读 RSS 不准?别信它
直接读 /proc/[pid]/statm 第二列看似是 RSS(单位为页),但它统计的是进程所有内存映射的**驻留页总数**,包含共享库、mmap 共享内存、甚至部分被其他进程引用的 copy-on-write 页——这根本不是你进程“独占”的物理内存。真正反映私有物理内存占用的是 RSS - Shared,而 statm 不提供共享部分的细分。
用 /proc/[pid]/smaps_rollup 拿到真正的私有 RSS
Linux 4.15+ 内核支持 smaps_rollup,它聚合了所有 VMA 的统计,并明确分离出 MMUPageSize 和 MMUPFPageSize 外的私有/共享项。关键字段是:
-
Private_Hugetlb:—— 可忽略(除非你显式用了大页) -
Private_Clean:+Private_Dirty:—— 这俩之和才是进程真正独占、未被共享的物理内存(单位:KB) -
SwapPss:等字段无关,不用看
示例代码片段(C++17,需 <fstream> + <string>):
long get_private_rss_kb(pid_t pid) {
std::ifstream f("/proc/" + std::to_string(pid) + "/smaps_rollup");
std::string line;
long priv_clean = 0, priv_dirty = 0;
while (std::getline(f, line)) {
if (line.rfind("Private_Clean:", 0) == 0) {
priv_clean = std::stol(line.substr(14));
} else if (line.rfind("Private_Dirty:", 0) == 0) {
priv_dirty = std::stol(line.substr(14));
}
}
return priv_clean + priv_dirty;
}
监控时注意 /proc/[pid]/smaps_rollup 的开销与权限
每次读取 smaps_rollup 都会触发内核遍历该进程全部 VMA 并做页表扫描,对大进程(如几十 GB 堆 + 大量 mmap)可能耗时数毫秒,频繁轮询(read 阻塞。另外:
立即学习“C++免费学习笔记(深入)”;
- 必须以与目标进程相同 UID 运行,或有
cap_sys_ptrace权限(root 或sudo setcap cap_sys_ptrace+ep ./monitor) - 若目标进程已退出,
/proc/[pid]目录消失,open()失败——得加errno == ENOENT判断 - 不要用
smaps(非 rollup 版)全量解析,它单次读取可能达 MB 级,且字段重复多,性能差一个数量级
macOS 和 Windows 怎么办?没有等价接口
macOS 没有暴露私有物理内存的直接指标:task_info(TASK_VM_INFO) 返回的 phys_footprint 是近似值(含压缩内存、pageout 预估),且从 macOS 10.15 起被标记为 deprecated;libproc.h 的 proc_taskinfo 里 pti_resident_size 实际是 RSS,不减共享。
Windows 上 GetProcessMemoryInfo 的 PRIVATE_USAGE 字段名有误导性——它其实是 WorkingSetSize - ShareablePages,但 ShareablePages 包含部分本应私有的页(如 DLL 中的只读数据段),实测比 Linux 的 Private_Clean+Dirty 高 5–15%。
跨平台监控必须接受:Linux 是唯一能拿到较精确私有 RSS 的系统;其他平台只能当趋势参考,不能用于内存泄漏精确定位。


















