最可靠方法是通过/proc/self/status读取vmrss和vmsize,结合mallinfo2()获取glibc堆近似统计,而精确的分配/释放总量必须通过ld_preload拦截malloc/free实现。

Linux下用/proc/self/status读取堆内存指标最可靠
Linux进程的堆使用情况不直接暴露给C++标准库,但内核通过/proc/self/status提供实时快照。关键字段是VmRSS(驻留物理内存)、VmSize(虚拟地址空间总量),而“堆分配总量”和“释放总量”没有直接对应项——它们属于glibc malloc内部统计,需额外手段获取。
常见误区是以为mallinfo()或malloc_stats()能给出精确动态值,实际它们只反映当前malloc管理器的粗略状态,且mallinfo()在glibc 2.33+已标记为废弃,malloc_info()输出XML格式,解析成本高。
-
VmRSS≈当前实际占用的物理内存(含堆、栈、共享库等,非纯堆) -
VmSize≈进程虚拟内存总大小(含未分配的brk空洞、mmap区域) - 真正堆分配/释放量只能靠拦截
malloc/free或启用malloc_hook(已弃用)或__malloc_hook(不推荐)
用mallinfo2()获取glibc堆管理器的近似统计
mallinfo2()是glibc 2.33新增的替代接口,返回struct mallinfo2,字段比旧mallinfo()更清晰,但仍不是实时计数器:它反映的是当前malloc arena中已分配但未归还给系统的内存块总和,不包括已free但尚未合并/返还的碎片,也不含通过mmap直接分配的大块(>128KB默认走mmap,不计入mallinfo)。
示例用法:
#include <malloc.h>
struct mallinfo2 mi = mallinfo2();
printf("allocated: %zu, free: %zu, mmaped: %zu\n",
mi.arena, mi.fsmblks, mi.hblkhd);
</malloc.h>
-
mi.arena:sbrk分配的主堆区字节数(≈累计分配减去已归还sbrk的部分) -
mi.fsmblks:fastbins中空闲块总大小(很小,通常可忽略) -
mi.hblkhd:通过mmap额外申请的堆内存字节数(大对象) - 这些值不随每次
malloc/free即时更新,而是按arena状态快照,且多线程下仅反映调用线程所在arena
想要精确跟踪分配/释放总量?必须自己hook或用LD_PRELOAD
标准库不提供全局累加计数器。若真需要“分配总量”“释放总量”这类运行时累加指标,唯一可行路径是拦截内存分配函数。推荐用LD_PRELOAD注入自定义malloc/free,而非修改源码或依赖编译器插桩。
最小可行方案示例(仅示意逻辑):
// my_malloc.c
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdatomic.h><p>static atomic_size_t total_alloc = ATOMIC_VAR_INIT(0);
static atomic_size_t total_free = ATOMIC_VAR_INIT(0);</p>
<p>static void<em> (</em>real_malloc)(size_t) = NULL;
static void (<em>real_free)(void</em>) = NULL;</p>
<p>void<em> malloc(size_t size) {
if (!real_malloc) real_malloc = dlsym(RTLD_NEXT, "malloc");
void</em> p = real_malloc(size);
if (p) atomic_fetch_add(&total_alloc, size);
return p;
}</p>
<p>void free(void* ptr) {
if (!real_free) real_free = dlsym(RTLD_NEXT, "free");
if (ptr) atomic_fetch_add(&total_free, malloc_usable_size(ptr));
real_free(ptr);
}</p></stdatomic.h></stdio.h></dlfcn.h>
- 注意
malloc_usable_size()返回实际分配大小(含malloc头),比用户请求值大,更接近真实开销 - 该方案无法捕获
new/delete,除非也hook它们;C++异常抛出路径中的临时分配可能被遗漏 - 多线程安全靠
atomic,但高频分配下原子操作本身有性能损耗
Windows上没有等价/proc,得用GetProcessMemoryInfo
Windows下PROCESS_MEMORY_COUNTERS_EX结构体提供PrivateUsage(近似VmRSS)、PagefileUsage等,但依然没有分离出“堆”这一层。CRT调试堆(_CrtDumpMemoryLeaks)仅用于调试模式泄漏检测,不提供运行时累加指标。
- Release模式下,
HeapWalk可遍历进程默认堆,但性能极差,不能高频调用 - 第三方方案如Detours可hook API,但复杂度远超Linux的LD_PRELOAD
- 结论:Windows端若需精确堆指标,基本只能接受采样级估算,或改用带监控能力的内存分配器(如tcmalloc、jemalloc)
实际部署时,多数场景只需VmRSS趋势 + mallinfo2()辅助判断是否存在明显内存增长。真正要拆解到“分配/释放总量”,意味着你已在做内存行为分析或写内存监控工具——这时候,自己hook是绕不开的一步,别指望标准接口给你现成数字。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











