Linux下如果知道一个进程究竟占用了多少内存?这是个经常被问道和被答错的问题。进程的内存分配是个比较复杂的话题,这里通过一个例子进行说明。
有这么一个简单程序:
[root@pczou pczou]# cat ./prog.c
#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>#define ONEM (1024*1024)
int func()
{
char s[16*ONEM];
char* p;
p = malloc(32*ONEM);
pause();
return 0;
}int main()
{
printf("pid: %d\n", getpid());
func();
return 0;
}
其中func()这个函数分配了32MB的内存,以及16MB的堆栈。
运行一下,prog会停在pause()的位置,看看ps怎么说:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 4238 0.0 0.0 52396 352 pts/0 S 21:29 0:00 ./prog
VSZ指的是进程内存空间的大小,这里是52396KB;
RSS指的是驻留物理内存中的内存大小,这里是352KB。
一般系统管理员知道VSZ并不代表进程真正用到的内存,因为有些空间会仅在页表中挂个名,也就是说只是虚拟存在着,只有真正用到的时候内核才会把虚拟页面和真正的物理页面映射起来。比如,prog.c中用malloc()分配的32MB内存,由于程序中并没有用到这些内存,所以不应算到进程的帐上。
进程的内存使用情况比较复杂,这是因为:
- 进程申请的内存不一定真正会被用到
- 真正用到的内存也不一定是只有该进程自己在用 (比如动态共享库)
所以酒足饭饱结帐的时候,酒吧打出的帐单中不应该有没有上的菜,也不应该一个菜两份钱。而ps给出的就是这样的“糊涂”帐单,不足为凭。
算清楚帐的唯一办法是把每个菜都仔细过一遍,看看有没有上,有没有重复。下面的帐单要清楚多了:
Virtual memory : 52396 KB
Effective VM : 52120 KB
Mapped : 352 KB
Effective mapped: 76.6 KB
Sole use : 72 KBPer file memory use
ld-2.3.4.so : VM 94208 B, M 90112 B, S 8192 B
prog : VM 8192 B, M 8192 B, S 8192 B
libc-2.3.4.so : VM 1180 KB, M 221184 B, S 16384 B
可以看出,虽然虚拟地址空间是52396KB,实际映射(a.k.a. 分配)的空间是352KB,这和ps给出的结果一致。再看"Effective Mapped"这个值,仅为76.6 KB。这个值的计算方法是:
有效的实际使用内存 = 该进程独占的内存 + 共享的内存A /共享A的进程数目 + 共享的内存B /共享B的进程数目 + …
虽然并不十分准确,但"Effective Mapped"已经足以说明进程所占用内存的实际大小了。
OK,最后用这个方法给所有进程都“结下帐”:
从上面的统计结果可以看出,
- 虽然firefox的占用虚拟空间是最大的,但其实际占用的内存却比X Server要少。
- firefox的实际占用的内存和其RSS (a.k.a. mapped)差别不大,只有少了约700;而kontact的实际占用的内存比起RSS足足少了约1.2MB。由此可以看出我用的窗口管理器是KDE而非Gnome,why? 因为Qt之类的共享库被很多KDE进程分担了。