Linux下无法直接获取精确堆分配总量与已释放量,最可靠方式是解析/proc/self/status中的VmRSS(物理驻留内存,KB)和VmData(数据段虚拟大小,KB),或用malloc_info()获取glibc堆内部状态,二者均非纯堆指标但具实用参考价值。

Linux下用/proc/self/status读取堆内存快照
Linux进程的堆使用情况不直接暴露给C++标准库,但内核通过/proc/self/status提供近似指标。其中VmSize是虚拟地址空间总量(含未映射页),VmRSS是物理驻留内存(含堆、栈、共享库等),而真正反映堆分配行为的是Heap相关字段——但注意:/proc/self/status里**没有单独的“堆分配总量”或“释放统计”字段**。
实际能稳定获取的是:
-
VmData:数据段大小(通常包含堆+全局变量,但brk和mmap分配的堆不一定全计入) -
VmStk:栈大小(可忽略) -
VmLib:共享库占用(无关)
更可靠的做法是解析/proc/self/maps中标记为[heap]的区域,计算其size(起始到结束的差值),这接近当前堆的已映射总量。但注意:它不等于malloc累计分配量,因为glibc的malloc会复用空闲块,且部分内存可能通过mmap单独分配(不在[heap]里)。
用mallinfo()或malloc_info()获取glibc堆内部状态
GNU libc提供两个关键接口:mallinfo()(旧,精度低)和malloc_info()(推荐,XML格式输出)。它们反映的是malloc管理器视角下的堆使用,而非OS级物理内存。
立即学习“C++免费学习笔记(深入)”;
mallinfo()返回结构体,其中:
-
arena:当前主分配区大小(字节) -
uordblks:已分配块总大小(≈当前堆上活跃对象总和) -
fordblks:空闲块总大小(即被free()但尚未归还OS的内存) -
hblkhd:通过mmap分配的堆外内存(如大块分配)
但mallinfo()在glibc 2.33+已被标记为废弃,且字段单位不统一(有的是字节,有的是块数),容易误读。更稳妥的是用malloc_info(0, fd)将堆状态写入文件描述符,再解析XML——它明确区分heap(brk区)、anonymous(mmap区),并给出各bin的使用/空闲计数。
无法直接获取“释放统计图表”的根本原因
C++运行时(包括glibc malloc)**不记录每次free()的调用时间、大小或调用栈**,也没有内置的累积计数器供查询“共释放多少次”或“释放总量”。所谓“释放统计”,必须由程序自己埋点实现。
常见做法是:
- 重载
operator new/operator delete,在每次分配/释放时累加全局计数器和字节数 - 用
LD_PRELOAD劫持malloc/free,记录调用栈(需backtrace()) - 接入
valgrind --tool=massif生成堆快照序列(非实时,但能出图表)
注意:重载操作符对静态存储期对象、STL容器内部分配无效;LD_PRELOAD无法拦截C++17的std::pmr分配器;massif会显著拖慢程序,仅适合离线分析。
生成图表需要自己聚合+导出数据
没有现成API返回“图表”,只有原始数据。要得到图表,你得:
- 定时(如每秒)调用
malloc_info()或解析/proc/self/status,把VmRSS、uordblks、fordblks存入数组 - 用
printf或JSON格式写入文件,再用Pythonmatplotlib或Gnuplot绘图 - 若需火焰图式调用栈分布,必须配合
perf record -e mem:swap_in,mem:swap_out或asan的堆栈报告
最易踩的坑是混淆概念:VmRSS下降≠内存被释放,可能是页面被换出;uordblks上升≠新分配,可能是碎片整理导致合并;malloc_info()输出的total字段是当前所有arena之和,但多线程下arena可能动态增减——这些细节不查源码很难确认。


















