Windows用GetProcessMemoryInfo获取WorkingSetSize(物理内存占用),Linux读/proc/self/status解析VmRSS;跨平台禁用mallinfo/malloc_stats,因其不涵盖mmap等内存且不可移植。

Windows 下用 GetProcessMemoryInfo 获取当前进程内存占用
这是最直接、开销最小的方式,适用于 Windows 平台的实时监控。你需要链接 psapi.lib,并包含 。
关键点在于使用 PROCESS_MEMORY_COUNTERS 结构体中的 WorkingSetSize(工作集大小,近似“物理内存占用”)或 PagefileUsage(页文件占用,反映总虚拟内存压力)。
-
WorkingSetSize更贴近用户感知的“占了多少内存”,但会受系统内存管理策略影响(如共享页、硬错误抖动) - 必须用
GetCurrentProcess()获取句柄,且该句柄默认有PROCESS_QUERY_INFORMATION权限 - 调用前建议先调用一次
GetProcessMemoryInfo检查返回值,失败时常见原因是权限不足或结构体cb字段未正确初始化
PROCESS_MEMORY_COUNTERS pmc;
pmc.cb = sizeof(pmc);
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
size_t kb = pmc.WorkingSetSize / 1024;
// kb 即当前工作集大小(KB)
}Linux 下读取 /proc/self/status 解析 VmRSS 和 VmSize
Linux 没有统一 API,标准做法是读取 /proc/self/status 文件并按行匹配关键词。这是稳定、无需额外权限、被几乎所有监控工具采用的方式。
VmRSS(Resident Set Size)对应物理内存实际占用,和 Windows 的 WorkingSetSize 最接近;VmSize 是虚拟地址空间总大小,含 mmap、stack、heap 等,但不含 swap。
立即学习“C++免费学习笔记(深入)”;
- 务必用
fopen("/proc/self/status", "r"),不能用std::ifstream默认构造——某些嵌入式或容器环境可能禁用 C++ iostream 的底层缓冲逻辑 - 逐行读取比一次性 fread 更安全,避免因字段顺序变化(如内核版本更新)导致解析错位
- 注意单位:所有
Vm*行末尾都是kB(含空格),需跳过最后 3 字符再strtol转换
FILE* f = fopen("/proc/self/status", "r");
char line[256];
while (fgets(line, sizeof(line), f)) {
if (strncmp(line, "VmRSS:", 6) == 0) {
long rss_kb = strtol(line + 7, nullptr, 10);
// rss_kb 即物理内存占用(KB)
break;
}
}
fclose(f);跨平台封装时为什么不要依赖 mallinfo 或 malloc_stats
这两个函数看似能获取堆内存统计,但实际不可靠:它们只反映 glibc malloc 分配器内部状态,不包括 mmap 分配的大块内存、C++ new 可能触发的直接系统调用、以及共享库/线程栈等非堆内存。
-
mallinfo在 glibc 2.33+ 已被标记为 deprecated,返回值语义模糊(如uordblks不含 mmap 区域) -
malloc_stats只打印到 stderr,无法程序化提取,且输出格式无保证(不同 glibc 版本差异大) - 在 musl libc(Alpine)、Android Bionic 等环境下根本不存在这些函数
想跨平台,宁可分别实现 Windows/Linux 路径,也不要试图用 malloc 层接口“取巧”。
高频采样时注意 /proc 文件读取的性能与一致性
每秒读多次 /proc/self/status 是可行的,但要注意两点:一是文件 I/O 开销虽小,但在高并发或低配设备上仍可能成为瓶颈;二是该文件是内核动态生成的快照,两次读取之间内存状态可能已变,不保证原子性。
- 如果只需要趋势数据,建议采样间隔 ≥ 100ms;低于 50ms 时,I/O 时间占比明显上升,且增量意义减弱
- 避免在信号处理函数(如
SIGUSR1handler)中直接读取 ——fopen/fgets不是 async-signal-safe 函数 - 若需更高精度,可考虑
mincore()手动检查页表(仅 Linux),但复杂度陡增,且对大内存进程代价很高
真正难的不是“怎么拿到数字”,而是理解每个数字背后代表哪一层抽象 —— 内存管理从来不是单一维度的事。










