用C程序计算进程总内存使用量结果偏大的问题排查咨询
进程内存统计的问题分析与解决办法
为什么你的计算结果偏大?
你总结的两个原因完全命中了核心:
- 内存页对齐的影响:
/proc/PID/maps里的地址范围是按内核页大小(你的是4KB)对齐的,每个段的大小都是页的整数倍,但进程实际用到的可能只是页内部分空间。直接计算地址差会把整个页都算进去,得到的是虚拟内存的总量,而非实际物理内存使用量。 - 未过滤已删除的内存段:标记为
(deleted)的段大多是文件被删除但进程还持有的映射,其中不少已经不再被访问,内核可能已经回收了对应的物理内存,但你的代码依然把它们全部计入总量,自然会虚高。
除此之外,你的代码还有几个容易忽略的问题:
- 错误处理不完整:当
space或hyphen为NULL时,代码没有终止当前循环,会触发未定义行为 - 没有区分共享与私有内存:带
s权限位的共享段是多个进程共用的,如果你要统计进程的独占内存,不应该把这些共享段全部算进去,否则会重复统计共享内存的总量
修正统计逻辑的几个方向
1. 优化/proc/PID/maps的处理
如果坚持用maps,需要做这些过滤:
- 跳过标记为
(deleted)的段(可通过检查行尾的(deleted)字符串判断) - 区分权限位:只统计带
p(私有)权限的段,或者对共享段做特殊处理(比如只计算进程实际修改过的私有页,但这点maps里看不到,得用smaps) - 跳过内核特殊映射:比如
[vdso]、[vsyscall]、[vvar]这类内核提供的虚拟映射,不属于进程的实际内存开销
2. 用/proc/PID/smaps获取更精准的统计
/proc/PID/smaps是比maps更详细的内存映射统计文件,每个段都包含了Rss(实际驻留物理内存)、Pss(比例共享内存,把共享部分按使用进程数拆分)、PrivateDirty(进程独占的脏页)等关键字段,是目前统计进程内存最靠谱的数据源之一。
比如你可以累加所有段的Pss值,得到进程实际“消耗”的内存(既包含独占部分,也合理分摊了共享部分)。下面是一个简单的实现示例:
#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "用法: %s <进程PID>\n", argv[0]); return 1; } char path[256]; snprintf(path, sizeof(path), "/proc/%s/smaps", argv[1]); FILE *fd = fopen(path, "r"); if (!fd) { perror("打开smaps文件失败"); return 1; } char *line = NULL; size_t len = 0; unsigned long total_pss = 0; unsigned long total_rss = 0; while (getline(&line, &len, fd) != -1) { // 提取PSS值(单位KB,转成字节) if (strncmp(line, "Pss:", 4) == 0) { unsigned long pss; sscanf(line + 4, "%lu", &pss); total_pss += pss * 1024; } // 提取RSS值(实际物理驻留内存) if (strncmp(line, "Rss:", 4) == 0) { unsigned long rss; sscanf(line + 4, "%lu", &rss); total_rss += rss * 1024; } } printf("总PSS内存: %lu 字节\n", total_pss); printf("总RSS内存: %lu 字节\n", total_rss); free(line); fclose(fd); return 0; }
3. 解析/proc/PID/status获取内核预统计值
如果你不想自己拆分内存段,可以直接读取/proc/PID/status文件,里面的字段都是内核已经统计好的:
VmRSS:进程实际驻留的物理内存大小(包含共享部分)VmSize:进程的虚拟内存总大小VmData:数据段(堆)的大小VmStk:栈的大小VmExe:可执行代码段的大小
比如读取VmRSS的逻辑很简单,只需要在文件中找到以VmRSS:开头的行,提取对应的数值即可。
关键注意事项
内存统计没有绝对的“最准确”,取决于你的需求:
- 如果要统计进程实际占用的物理内存,
PSS是最合理的指标,它避免了共享内存的重复统计 - 如果要统计进程的虚拟内存空间总量,
VmSize或过滤后的maps计算结果更合适 - 对于
(deleted)的段,不要直接全部剔除,有些可能还在被进程使用,最好结合smaps里的Rss值判断,如果Rss为0再忽略
内容的提问来源于stack exchange,提问作者Mathew
相关产品推荐
相关产品推荐

