最直接的驻留量与数据段估算方式是读取/proc/self/status中的vmrss和vmdata;vmrss表示物理内存中实际驻留的字节数,vmdata表示数据段虚拟内存大小,二者单位均为kb,但均非精确堆内存指标。

Linux 下用 /proc/self/status 读取 VmRSS 和 VmData 是最直接的驻留量与数据段估算方式
Linux 内核通过 /proc/self/status 暴露当前进程的内存快照,其中 VmRSS(Resident Set Size)表示当前实际驻留在物理内存中的字节数,即你常说的“驻留量”;VmData 表示数据段(含堆、BSS、动态分配空间)的虚拟内存大小,虽不等于堆分配总量,但对长期运行程序的堆增长趋势有参考价值。
注意:VmRSS 包含所有匿名映射(如 malloc、mmap(MAP_ANONYMOUS))、共享库数据页、甚至部分栈页,不能精确等同于“堆驻留量”,但它是最接近、开销最低的可观测指标。
实操建议:
- 用
std::ifstream打开/proc/self/status,逐行匹配以VmRSS:或VmData:开头的行,提取后接的数字(单位 KB) - 不要依赖
VmStk或VmLib来反推堆 —— 它们边界模糊且受 ASLR、共享库加载顺序影响 - 若需毫秒级采样,避免频繁打开/解析该文件(内核每次读取会构造临时状态),可考虑每秒最多 10 次
无法跨平台获取“已释放但未归还给 OS 的堆内存”——malloc 实现决定行为
glibc 的 ptmalloc2 在小块释放后通常保留在 fastbins 或 unsorted bin 中供复用,大块(≥ 128KB)才可能调用 brk 或 mmap(MAP_ANONYMOUS) 并在 free 后尝试 munmap。但“是否归还”由 malloc_trim 触发,且仅对 sbrk 区域有效;mmap 分配的大块则在 free 时立即 munmap。
这意味着:“已释放总量”没有定义 —— C++ 标准不规定释放语义,operator delete 只保证对象析构和内存可重用,不承诺归还 OS。
实操建议:
- 想观测 glibc 堆内部状态,可用
mallinfo()(已废弃但仍可用)或更推荐的malloc_info(0, FILE*)输出 XML 格式,解析其中<heap></heap>下的total_heap_size、system_bytes(向 OS 申请总量)、in_use_bytes(当前malloc返回但未free的量) -
malloc_info输出需自己解析,system_bytes - in_use_bytes近似为“已释放但未归还量”,但该差值包含 arena 管理开销、对齐填充,不可直接当字节数信任 - Windows 下无等价接口,
HeapWalk只适用于私有堆,且不暴露释放块的归还状态
用 __builtin_frame_address(0) + sbrk(0) 估算当前堆顶位置(仅限 glibc + x86_64)
虽然不能直接拿到“分配总量”,但可通过比较堆顶地址与栈底地址粗略判断堆使用宽度。前提是:你的程序没大量使用 mmap 分配堆外内存(如 jemalloc/tcmalloc 默认行为),否则该方法失效。
实操建议:
- 调用
sbrk(0)获取当前brk位置;用__builtin_frame_address(0)获取当前栈帧地址,再递归向下走几层(或查/proc/self/maps找[stack]范围)估算栈底;二者差值是用户态虚拟地址空间中“栈-堆夹层”的大致跨度 - 该值波动大:线程栈、VDSO、动态库加载都挤占地址空间,仅适合调试时快速判断是否疑似堆泄漏(比如该差值数分钟内持续扩大)
- 绝对不要在生产环境依赖此法做监控 —— 它不是 ABI 稳定接口,ARM64 上
__builtin_frame_address行为不同,musl libc 不支持sbrk
真正可控的方案:自己接管 new/delete 并计数
如果你必须精确知道“当前有多少字节处于已分配未释放状态”,唯一可靠方式是拦截所有堆分配路径。标准库不提供钩子,但你可以重载全局 operator new 和 operator delete,配合原子计数器统计。
实操建议:
- 用
std::atomic<size_t></size_t>记录累计分配字节数与当前活跃字节数,new时加,delete时减;注意nothrow版本、数组版本(operator new[](size_t))都必须重载 - 别忘了
std::malloc/std::free—— 如果代码混用 C 风格内存管理,需用LD_PRELOADhook 或链接时替换,否则计数漏掉 - 多线程下原子操作开销明显,若只用于诊断,可加编译宏控制(如
#ifdef MEMTRACE),发布版关闭
所有系统级指标都是估算,只有你自己拦截的分配点才能给出确定性答案。但代价是侵入性强、难以覆盖第三方库,以及无法区分“逻辑上应释放但未释放”和“还在用只是暂时没 free”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











