分类: LINUX
2012-04-02 21:12:44
进程XXX占用了多少内存?这是个经常被问到,也经常被答错的问题。进程的内存分配是个比较复杂的话题,而Linux上的工具往往把这个问题过分简单化,因此引出不少误解和困惑。首先把ps, top这类工具扔掉,然后看这么一个简单程序:
[root@pczou pczou]# cat ./prog.c
#i nclude
#i nclude
#i nclude
#i nclude
#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程序,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 KB
Per 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的进程数目 + ...
比如对于一个kde应用程序kontact,它用的Qt库的虚拟地址空间为 7M,而实际映射的空间有4.5M,也就是说真正给 Qt分配物理内存大小为4.5M。假设有10个KDE应用正在运行,那么记到kontact帐上的就不应该是4.5M,而是A-A之后的0.45M。这么算帐虽然并不十分准确,但"Effective Mapped"已经足以说明进程所占用内存的实际大小了。
OK,最后用这个方法给系统中所有进程都“结下帐”:
从上面的统计结果可以看出:
虽然firefox的占用虚拟空间是最大的,但其实际占用的内存却比X Server要少。
firefox 的实际占用的内存和其RSS (a.k.a. mapped)差别不大,占RSS的99%;而kontact的实际占用内存却仅占RSS的63%,有27%的内存是共享的。由此可以粗略看出我用的窗口管理器是KDE而非Gnome,why? 因为Qt之类的共享库被很多KDE进程分担了。
sole值可以理解为"private mapped",也就是这个进程退出后可能被释放的内存 (对于非匿名的映射页面可能还会存留一段时间)。