Linux无“线程级RSS”,/proc/[pid]/task/[tid]/status中的VmRSS是进程全局快照;定位高内存线程需通过malloc hook记录线程ID分配行为,或perf追踪内存访问模式。

Linux 下没有“线程级 RSS”这个概念
内核不为线程单独维护驻留物理内存(RSS)统计,/proc/[pid]/status 和 /proc/[pid]/statm 中的 VmRSS 是整个进程的,包含所有线程共享的堆、代码段、共享库、mmap 区域等。即使你用 pthread_create 启了 100 个线程,VmRSS 也不会按线程拆分——它只反映该进程当前锁住在物理内存中的总页数。
常见错误现象:试图读取 /proc/[pid]/task/[tid]/status 并查找 VmRSS,结果发现每个线程的值和进程主 status 几乎一致,甚至略小几 KB(仅因栈大小微差),误以为能定位“哪个线程吃内存”。其实这只是内核对每个 task_struct 的冗余快照,不是独立统计。
想定位高内存消耗线程?得从堆分配源头下手
真正可操作的路径是:把内存分配行为和线程 ID 关联起来。glibc 不提供线程粒度的 mallinfo,但你可以:
- 用
LD_PRELOAD劫持malloc/free/realloc,在 wrapper 中用std::this_thread::get_id()记录每次调用的线程 ID 和 size,并原子累加到线程局部计数器(如std::atomic<size_t></size_t>数组或std::unordered_map+ 读写锁) - 启用 glibc 的
malloc_info(0, fd)输出 XML,其中<heap>节点带nr_threads和total字段,但注意:它只告诉你 arena 总用量,不标记归属线程;多线程下需配合perf record -e syscalls:sys_enter_mmap追踪 mmap 分配者 - 避免用
mallinfo:它返回的是主 arena 数据,且在 glibc 2.33+ 已被标记为 deprecated;uordblks值常远低于VmRSS,因它不统计 mmap 分配的大块(比如std::vector超过 128KB 可能走 mmap)
调试时快速筛查可疑线程栈和 mmap 区域
虽然不能直接看“线程 RSS”,但可以交叉验证哪些线程可能拖累内存:
立即学习“C++免费学习笔记(深入)”;
- 用
cat /proc/[pid]/task/[tid]/stack查线程调用栈,若某线程长期卡在__libc_malloc或new路径,结合其栈大小(Threads:行)和Stack字段(单位 KB),可初步判断是否栈溢出或递归分配 - 用
cat /proc/[pid]/task/[tid]/maps | awk '$6 ~ /^heap$/ {sum += $2-$1} END {print sum/1024 " KB"}'粗略估算该线程视角下的 heap 映射范围——但这只是虚拟地址空间,不代表已驻留物理页 - 更准的做法:用
cat /proc/[pid]/smaps_rollup | grep -E '^(Private_|Rss:|Pss:)' | awk '{sum += $2} END {print sum " KB"}'获取进程总私有物理内存,再用perf script配合-e mem-loads,mem-stores采样,过滤出thread_id高频触发 page fault 的线程
高频监控别碰 /proc/[pid]/task/[tid]/status
每秒读上百个线程的 status 文件会显著增加内核 task_stat 锁争用,实测在 32 核机器上单次读取延迟可达 0.5–2ms。真实服务中应:
- 只在诊断阶段临时启用,比如收到 OOM 信号后,用
sigaction捕获并 dump 所有task/[tid]/stat中的rss字段(该字段是页数,非 KB) - 生产环境监控优先用进程级
VmRSS+ 自研 malloc hook 计数器,二者偏差超过 20% 时再触发线程级深度分析 - 不要解析
/proc/[pid]/task/[tid]/statm:它的格式与进程级statm完全相同,第一列size是虚拟内存,第二列resident是物理页数,但该值是进程全局的快照,不是线程独占
/proc 文件系统里直接扒出“线程 RSS”的方案,本质都是在读同一个数字的不同副本。


















