vmpeak是linux进程自启动以来的最大虚拟内存值(单位kb),读取/proc/self/status最准确高效;windows无直接对应指标,可用peakpagefileusage近似。

Linux 下用 /proc/self/status 读取 VmPeak
Linux 内核会在进程的 /proc/self/status 文件中实时记录内存使用统计,其中 VmPeak 字段就是自进程启动以来达到过的最大虚拟内存(含堆、栈、映射等),对多数场景已足够近似堆分配峰值。注意:它不是纯堆(heap)专用值,但实际中堆增长是虚拟内存增长的主因,且无侵入、零开销。
实操建议:
- 用
std::ifstream打开/proc/self/status,逐行读取,匹配以VmPeak:开头的行 - 提取后的数值单位是
kB,需乘以 1024 转为字节;部分内核版本可能用KB,建议忽略大小写或统一 trim 空格后比对 - 该值在 fork 后子进程独立计数,多线程安全——因为只读文件,不涉及共享状态
- 不要在信号处理函数中调用,避免
read()可重入问题;常规同步上下文调用即可
Windows 下调用 GetProcessMemoryInfo 查 PeakPagefileUsage
Windows 没有直接对应 VmPeak 的 API,最接近的是 PROCESS_MEMORY_COUNTERS_EX::PeakPagefileUsage,它表示进程生命周期中页文件(pagefile)占用峰值,能较合理反映堆压力(尤其当物理内存不足时堆被迫换出)。
实操建议:
- 需包含
<psapi.h></psapi.h>,链接Psapi.lib;运行时确保psapi.dll可加载(Win XP SP1+) - 调用前用
GetCurrentProcess()获取句柄,传给GetProcessMemoryInfo() -
PeakPagefileUsage单位是字节,无需转换;但它包含所有私有提交内存(含堆、私有数据段、未映射但已VirtualAlloc的区域),不是严格堆专属 - 若程序大量使用
VirtualAlloc+MEM_RESERVE但未MEM_COMMIT,该值不会增加——这点和 LinuxVmPeak行为不同
跨平台精确监控堆需 hook malloc/new(慎用)
如果必须区分“纯堆”(如 malloc / operator new 分配)且排除 mmap、栈、代码段等,唯一办法是拦截内存分配入口。但这会引入性能开销、线程安全风险和 ABI 兼容问题。
实操建议:
- Linux 可用
LD_PRELOAD注入自定义malloc、free实现;Windows 可用 DLL 注入或链接时替换malloc符号(需静态链接 CRT 或用/NODEFAULTLIB) - 维护一个原子累加器记录当前已分配字节数,每次
malloc成功后更新峰值;注意realloc需先减旧 size 再加新 size - 务必处理多线程竞争:
std::atomic<size_t></size_t>用于当前用量,另用互斥锁保护峰值比较逻辑(因峰值更新频次低,锁开销可接受) - 无法捕获 C++ 标准库内部使用的堆(如
std::string小字符串优化除外)、第三方库绕过 malloc 直接调用mmap的情况
为什么不用 mallinfo 或 malloc_stats?
mallinfo 在 glibc 2.33+ 已被标记为 deprecated,返回结构体中 hblkhd(堆内存总量)和 usmblks(已分配块大小)都不反映历史峰值;malloc_stats() 只打印到 stderr,无法提取数值,且输出格式不稳定(不同 glibc 版本字段顺序/含义可能变)。
更关键的是:它们只统计 glibc malloc 管理的 heap segment,不包括通过 mmap 直接申请的大块内存(>128KB 默认走 mmap),而现代 C++ 分配器(如 jemalloc、tcmalloc)或 std::pmr::monotonic_buffer_resource 完全不走这套路径。
所以除非你明确锁定使用老版本 glibc 且只用 malloc,否则别依赖这两个接口。
VmPeak / PeakPagefileUsage 足够),还是特定分配器的内部堆行为(那就得进源码或换支持 introspection 的 allocator)。后者几乎必然伴随构建和部署复杂度上升。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











