在Windows平台做C++性能分析时,准确掌握进程的物理内存占用峰值对排查内存泄漏和评估资源消耗非常关键。系统为每个进程维护了一份内存计数器,其中物理内存相关的峰值数据可以通过PSAPI中的GetProcessMemoryInfo函数读取。

一、核心API与数据结构解析
GetProcessMemoryInfo定义在Psapi.h中,需要链接Psapi.lib。该函数接收进程句柄、指向内存计数器结构的指针以及结构大小,将当前进程的内存统计信息填入结构中。我们通常使用扩展结构PROCESS_MEMORY_COUNTERS_EX,它兼容基础结构并在末尾增加了一些字段。
结构中的PeakWorkingSetSize成员以字节为单位,表示进程自启动以来工作集(即驻留物理内存)的最高值。工作集就是操作系统真正放在物理内存里供进程使用的页面集合,因此它直接反映物理内存占用峰值。与之容易混淆的PeakPagefileUsage记录的是页文件提交峰值,属于虚拟内存范畴,并非物理占用。
1.1 关键字段说明
除了PeakWorkingSetSize,结构中还有WorkingSetSize(当前工作集)、QuotaPagedPoolUsage(分页池配额)等。实战中我们重点关注峰值字段,因为它能告诉我们程序在压力最大时消耗了多少物理资源。
在x64系统上,这些Size字段是SIZE_T类型,宽度随平台变化。用%llu打印SIZE_T时,建议在32位下做强制转换,避免格式符不匹配导致输出异常。
二、实战代码演示
下面给出一个完整的控制台程序,演示如何获取并打印当前进程的physical memory peak。代码以文本形式展示,所有小于号和大于号均已转义。
#include <windows.h>
#include <psapi.h>
#include <stdio.h>
#pragma comment(lib, "psapi.lib")
int main() {
HANDLE hProcess = GetCurrentProcess();
PROCESS_MEMORY_COUNTERS_EX pmc;
pmc.cb = sizeof(pmc);
if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
SIZE_T peakWS = pmc.PeakWorkingSetSize;
printf("Physical memory peak (bytes): %llun", (unsigned long long)peakWS);
printf("Physical memory peak (MB): %llun", (unsigned long long)(peakWS / (1024 * 1024)));
} else {
printf("GetProcessMemoryInfo failed, error: %lun", GetLastError());
}
return 0;
}
上述代码首先通过GetCurrentProcess获取进程句柄,该句柄无需关闭。然后将cb字段设为结构大小,这是Windows API常见的版本控制方式。调用成功后,PeakWorkingSetSize即所需峰值。
若需要监控其他进程,应使用OpenProcess请求PROCESS_QUERY_INFORMATION权限,并在使用后CloseHandle。对系统服务或保护进程可能无权打开,需做好错误处理。
2.1 监控外部进程示例
以下片段展示如何附加到指定PID并读取其峰值。注意权限不足时会返回NULL句柄。
#include <windows.h>
#include <psapi.h>
#include <stdio.h>
#pragma comment(lib, "psapi.lib")
void print_peak(DWORD pid) {
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (!h) {
printf("open fail %lun", GetLastError());
return;
}
PROCESS_MEMORY_COUNTERS_EX pmc;
pmc.cb = sizeof(pmc);
if (GetProcessMemoryInfo(h, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
printf("pid %lu peak MB: %llun", pid, (unsigned long long)(pmc.PeakWorkingSetSize / (1024*1024)));
}
CloseHandle(h);
}
三、常见误区与注意点
一个典型误区是把PrivateUsage或PagefileUsage当作物理内存峰值。PrivateUsage反映进程独占的提交内存,可能很大一部分还在页文件或待分配状态,并不能说明物理占用。只有WorkingSet系列才和物理内存直接相关。
另一个注意点是峰值只增不减。系统记录的是历史最高,因此若想测量某段逻辑的内存高峰,应重启进程或在该逻辑前记录初始值,用当前峰值减去基线做区间评估,否则会包含启动期加载的库内存。
3.1 多平台差异
本文方法仅适用于Windows。Linux下应读取/proc/self/status中的VmHWM字段,macOS则用task_info的phys_footprint。跨平台项目建议封装统一接口,根据宏区分实现。
此外,在WSL或容器环境里,GetProcessMemoryInfo看到的是宿主视角的占用,可能和cgroup限制不一致,需要结合其他手段做资源约束分析。
四、小结
通过GetProcessMemoryInfo配合PROCESS_MEMORY_COUNTERS_EX,C++开发者能以极低开销拿到准确的物理内存占用峰值。核心就是读取PeakWorkingSetSize,并理解它和虚拟内存指标的区别。实战中注意句柄权限、字段类型与峰值单调性,就能把这套逻辑稳稳嵌入性能监控模块。
当你需要长期采样时,可起一个独立线程周期性调用该函数,将峰值上报到日志或监控面板,以及时发现内存水位异常上涨的趋势。
C++GetProcessMemoryInfophysical_memory_peak修改时间:2026-08-06 05:18:28