真正可靠的运行时内存峰值需基于堆分配行为聚合统计,核心路径只有两条:hook系统堆api(如heapalloc)或劫持c运行时malloc/free调用;getprocessmemoryinfo因受系统调度干扰、含共享内存且采样率低,无法反映真实堆内存峰值。

直接看进程的 WorkingSetSize 或 PagefileUsage 并不能反映真实内存峰值——它们是瞬时快照,且受系统调度干扰大。真正可靠的运行时内存峰值,必须基于堆分配行为做聚合统计,核心路径只有两条:Hook 系统堆 API(如 HeapAlloc),或劫持 C 运行时的 malloc/free 底层调用。
为什么 GetProcessMemoryInfo 不够用
GetProcessMemoryInfo 返回的是当前工作集(WorkingSetSize)和私有字节(PrivateUsage),但这两个值:
- 受系统内存压力自动收缩,比如后台触发内存压缩或页面换出,数值会突然下跌,导致峰值被掩盖
- 包含共享 DLL 的内存、映射文件等非堆内容,和你代码里
new出来的对象无直接对应关系 - 采样频率低(通常需手动轮询),极易错过短时爆发的分配高峰
它适合监控服务级水位,不适合定位“这段逻辑到底占了多少堆内存”。
Hook HeapAlloc/HeapFree 是最稳的 Windows 方案
Windows 下所有 malloc、new、LocalAlloc 最终都落到 HeapAlloc(默认进程堆),Hook 它能 100% 覆盖用户态堆分配。关键点:
- 必须 Hook
HeapAlloc、HeapReAlloc、HeapFree三个函数,缺一不可;HeapReAlloc处理 resize 场景,漏掉会导致统计失真 - 使用
Microsoft Detours库比手写 inline hook 更安全,避免线程竞争时修改跳转指令引发崩溃 - 记录每块内存的 size + 调用栈(用
RtlCaptureStackBackTrace),否则只知道“峰值多少”,不知道“谁干的” - 多线程下所有计数操作必须加锁,推荐
SRWLOCK(轻量)而非CriticalSection(开销大)
示例钩子片段(简化):
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
SIZE_T g_currentHeapUsage = 0;
SIZE_T g_peakHeapUsage = 0;
SRWLOCK g_lock = SRWLOCK_INIT;
<p>LPVOID WINAPI MyHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) {
LPVOID ptr = RealHeapAlloc(hHeap, dwFlags, dwBytes);
if (ptr) {
AcquireSRWLockExclusive(&g_lock);
g_currentHeapUsage += dwBytes;
g_peakHeapUsage = max(g_peakHeapUsage, g_currentHeapUsage);
ReleaseSRWLockExclusive(&g_lock);
}
return ptr;
}
</p>
重载 new/delete 容易踩的坑
看似简单,但实际在 Release 版本中极易失效:
- 编译器可能内联
operator new,尤其小对象( - STL 容器(如
std::vector)内部可能直接调用malloc,不走new,导致漏统计 - 第三方库(如 Boost、OpenSSL)链接的是静态 CRT,你的重载对它们完全无效
- 没处理
nothrow版本和数组版本(operator new[]),会漏掉大量分配
如果坚持用重载,至少补全这四个:
void* operator new(size_t size); void* operator new[](size_t size); void* operator new(size_t size, const std::nothrow_t&) noexcept; void* operator new[](size_t size, const std::nothrow_t&) noexcept;
峰值数据怎么导出才实用
只存一个数字没意义,要能回溯上下文:
- 不要只记峰值时刻的总字节数,同时记录当时最大的 N 个分配块(按 size 排序),附带调用栈前 5 帧
- 提供手动打点接口,比如
MemPeakSnapshot("network_decode"),把峰值和业务阶段绑定 - 输出格式用 JSON 而非纯文本,方便后续用 Python 或 Grafana 解析绘图
- 避免日志写磁盘——高频分配下 I/O 会拖垮性能,改用环形内存缓冲区 + 进程退出时 dump
最常被忽略的一点:Hook 后的内存统计本身也要计入进程内存。Detours 注入的代码、你维护的分配表、调用栈缓存……这些都在吃内存。如果峰值接近 2GB,得确认这部分开销是否已排除在外。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










