前几天小马同学问了一个问题:
我ps aux看到的RSS内存只有不到30M,但是免费看到内存却已经使用了7,8G了,已经开始swap了,请问ps aux的实际物理内存统计是不是漏了其中的内存没算?有什么办法确定免费中已使用的内存都去哪儿了呢?
这个问题不止一个同学遇到过了,之前小王同学也遇到这个问题,内存的计算总是一个迷糊账。我们今天来把它算个清楚下!
通常我们是这样看内存的剩余情况的:
<span style="color:#3e3e3e"><code>$<span style="color:#f82375">free</span> -m
total used <span style="color:#f82375">free</span> shared buffers cached
Mem: <span style="color:#ae87fa">48262</span> <span style="color:#ae87fa">7913</span> <span style="color:#ae87fa">40349</span> <span style="color:#ae87fa">0</span> <span style="color:#ae87fa">14</span> <span style="color:#ae87fa">267</span>
-/+ buffers/cache: <span style="color:#ae87fa">7631</span> <span style="color:#ae87fa">40631</span>
Swap: <span style="color:#ae87fa">2047</span> <span style="color:#ae87fa">336</span> <span style="color:#ae87fa">1711</span></code></span>
那么这个信息是如何解读的呢,下面这个图解释的挺清楚的!
上面的情况下我们总的内存有48262M,用掉了7913M。其中buffer + cache共14 + 267 = 281M,由于这种类型的内存是可以回收的,虽然我们用掉了7913M,但是实际上我们如果实在需要的话,这部分buffer / cache内存是可以放出来的。
我们来演示下:
<span style="color:#3e3e3e"><code>$ sudo sysctl vm.drop_caches=<span style="color:#ae87fa">3</span>
vm.drop_caches = <span style="color:#ae87fa">3</span>
$ free -m
total used free shared buffers cached
<span style="color:#ae87fa">Mem:</span> <span style="color:#ae87fa">48262</span> <span style="color:#ae87fa">7676</span> <span style="color:#ae87fa">40586</span> <span style="color:#ae87fa">0</span> <span style="color:#ae87fa">3</span> <span style="color:#ae87fa">41</span>
-<span style="color:inherit">/+ buffers/cache</span>: <span style="color:#ae87fa">7631</span> <span style="color:#ae87fa">40631</span>
<span style="color:#ae87fa">Swap:</span> <span style="color:#ae87fa">2047</span> <span style="color:#ae87fa">336</span> <span style="color:#ae87fa">1711</span></code></span>
我们把buffer / cache大部分都清除干净了,只用了44M,所以我们这次使用的空间是7676M。
到现在我们比较清楚几个概念:
1.总的内存多少
2。buffer / cache内存可以释放的
3. used的内存的概率。
即使是这样我们还是要继续追查下使用的空间(7637M)到底用到哪里去了?
这里首先我们来介绍下nmon这个工具,它对内存的使用显示比较直观。
使用的内存的去向我们很自然的就想到操作系统系统上的各种进程需要消耗各种内存,我们透过top工具来看下:
通常我们会看进展的RES这一项,这到底是什么意思呢?这个数字从哪里出来的呢?通过strace对top和nmon的追踪和结合源码,我们确定这个值是从/ proc / PID / statm的第二个字段读取出来的。
那这个字段什么意思呢?
男人PROC
或者http://www.kernel.org/doc/man-pages/online/pages/man5/proc.5.html 会详细说明/ proc /下一个文件的具体意思,我们摘抄下:
<span style="color:#3e3e3e"><code>/proc/[pid]/statm
Provides information about memory usage, measured in pages. The
columns are:
size total program size
(same as VmSize in /proc/[pid]/status)
resident resident <span style="color:#f82375">set</span> <span style="color:#f82375">size</span>
(same <span style="color:#f82375">as</span> VmRSS <span style="color:#f82375">in</span> /proc/[pid]/<span style="color:#f82375">status</span>)
<span style="color:#f82375">share</span> <span style="color:#f82375">shared</span> pages (<span style="color:#f82375">from</span> <span style="color:#f82375">shared</span> mappings)
<span style="color:#f82375">text</span> <span style="color:#f82375">text</span> (code)
lib <span style="color:#f82375">library</span> (<span style="color:#f82375">unused</span> <span style="color:#f82375">in</span> Linux <span style="color:#ae87fa">2.6</span>)
<span style="color:#f82375">data</span> <span style="color:#f82375">data</span> + stack
dt dirty pages (<span style="color:#f82375">unused</span> <span style="color:#f82375">in</span> Linux <span style="color:#ae87fa">2.6</span>)</code></span>
居民集大小也就是每个进程用了特定的多少页的内存。由于linux系统采用的是虚拟内存,进程的代码,库,堆和栈使用的内存都会消耗内存,但是申请出来的内存,只要没真正的触摸过,是不算的,因为没有真正为之分配物理页面。
我们实际进程使用的物理页面应该用resident set size来算的,遍历所有的进程,就可以知道所有的所有的进程使用的内存。
我们来实验下RSS的使用情况:
<span style="color:#3e3e3e"><code>$ cat RSS.sh
<span style="color:#808080">#/bin/bash </span>
<span style="color:#f82375">for</span> PROC <span style="color:#f82375">in</span> `ls /proc/|grep <span style="color:#eedc70">"^[0-9]"</span>`
<span style="color:#f82375">do</span>
<span style="color:#f82375">if</span> [ -f /proc/<span style="color:#629755">$PROC</span>/statm ]; <span style="color:#f82375">then</span>
TEP=`cat /proc/<span style="color:#629755">$PROC</span>/statm | awk <span style="color:#eedc70">'{print ($2)}'</span>`
RSS=`expr <span style="color:#629755">$RSS</span> + <span style="color:#629755">$TEP</span>`
<span style="color:#f82375">fi</span>
<span style="color:#f82375">done</span>
RSS=`expr <span style="color:#629755">$RSS</span> \* 4`
<span style="color:#f82375">echo</span> <span style="color:#629755">$RSS</span><span style="color:#eedc70">"KB"</span>
$ ./RSS.sh
7024692KB</code></span>
从数字来看,我们的进程使用了大约7024M内存,距离7637M还有几百M内存哪里去了?哪里去了?猫吃掉了?
我们再回头来仔细看下nmon的内存统计表。
那个那个死的slab是什么呢?那个PageTables又是什么呢?
简单的说内核为了高性能每个需要重复使用的对象都会有个池,这个slab池会缓存大量常用的对象,所以会消耗大量的内存。
<span style="color:#3e3e3e"><code>$ slabtop</code></span>
我们可以看到:
从图我们可以研磨各种对象的大小和数目,遗憾的是没有告诉我们slab消耗了多少内存。
我们自己来算下好了:
<span style="color:#3e3e3e"><code><span style="color:#5bdaed">$</span><span style="color:inherit"> echo `cat /proc/slabinfo |awk 'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'` MB</span>
904.256 MB</code></span>
好吧,把每个对象的数量*大小,再累加,我们就得到了总的内存消耗量:904M
那么PageTables呢?我们万能的内核组的同学现身了:
<span style="color:#3e3e3e"><code>伯瑜:
你还没有计算page tables的大小,还有<span style="color:inherit">struct page也有一定的大小(每个页一个,64bytes),如果是2.6.32的话,每个页还有一个page_cgroup(32bytes),也就是说内存大小的2.3%(96/4096)会被内核固定使用的
含黛:</span>
<span style="color:inherit">struct page是系统boot的时候就会根据内存大小算出来分配出去的,18内核是1.56%左右,32内核由于cgroup的原因会在2.3%</span></code></span>
好吧,知道是干嘛的啦,管理这些物理页面的硬开销,那么具体是多少呢?
<span style="color:#3e3e3e"><code><span style="color:#5bdaed">$</span><span style="color:inherit"> echo `grep PageTables /proc/meminfo | awk '{print $2}'` KB</span>
58052 KB</code></span>
好吧,小结下!内存的去向主要有3个:
-
1.进程消耗。
-
2.平板消耗
-
3.pagetable消耗。
我把三种消耗汇总下和自由出的结果比对下,这个脚本的各种计算项仲同学帮忙搞定的:
<span style="color:#3e3e3e"><code>$ cat cm.sh
<span style="color:#808080">#/bin/bash</span>
<span style="color:#f82375">for</span> PROC <span style="color:#f82375">in</span> `ls /proc/|grep <span style="color:#eedc70">"^[0-9]"</span>`
<span style="color:#f82375">do</span>
<span style="color:#f82375">if</span> [ -f /proc/<span style="color:#629755">$PROC</span>/statm ]; <span style="color:#f82375">then</span>
TEP=`cat /proc/<span style="color:#629755">$PROC</span>/statm | awk <span style="color:#eedc70">'{print ($2)}'</span>`
RSS=`expr <span style="color:#629755">$RSS</span> + <span style="color:#629755">$TEP</span>`
<span style="color:#f82375">fi</span>
<span style="color:#f82375">done</span>
RSS=`expr <span style="color:#629755">$RSS</span> \* 4`
PageTable=`grep PageTables /proc/meminfo | awk <span style="color:#eedc70">'{print $2}'</span>`
SlabInfo=`cat /proc/slabinfo |awk <span style="color:#eedc70">'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'</span>`
<span style="color:#f82375">echo</span> <span style="color:#629755">$RSS</span><span style="color:#eedc70">"KB"</span>, <span style="color:#629755">$PageTable</span><span style="color:#eedc70">"KB"</span>, <span style="color:#629755">$SlabInfo</span><span style="color:#eedc70">"MB"</span>
<span style="color:#f82375">printf</span> <span style="color:#eedc70">"rss+pagetable+slabinfo=%sMB\n"</span> `<span style="color:#f82375">echo</span> <span style="color:#629755">$RSS</span>/1024 + <span style="color:#629755">$PageTable</span>/1024 + <span style="color:#629755">$SlabInfo</span>|bc`
free -m
$ ./cm.sh
7003756KB, 59272KB, 904.334MB
rss+pagetable+slabinfo=7800.334MB
total used free shared buffers cached
Mem: 48262 8050 40211 0 17 404
-/+ buffers/cache: 7629 40633
Swap: 2047 336 1711</code></span>
free报告说7629,我们的cm脚本报告说7800.3M,我们的CM多报了171M。
该死,这又怎么回事呢?
我们重新校对下我们的计算。我们和nmon来比对下,slab和pagetable的值是吻合的。那最大的问题可能在进展的消耗计算上。
resident resident set size包括我们使用的各种库和so等共享的模块,在前面的计算中我们重复计算了。
<span style="color:#3e3e3e"><code>$ pmap `pgrep bash`
...
22923: -bash
0000000000400000 848K r-x<span style="color:#808080">-- /bin/bash</span>
00000000006d3000 40K rw<span style="color:#808080">--- /bin/bash</span>
00000000006dd000 20K rw<span style="color:#808080">--- [ anon ]</span>
00000000008dc000 36K rw<span style="color:#808080">--- /bin/bash</span>
00000000013c8000 592K rw<span style="color:#808080">--- [ anon ]</span>
000000335c400000 116K r-x<span style="color:#808080">-- /lib64/libtinfo.so.5.7</span>
...
0000003ec5220000 4K rw<span style="color:#808080">--- /lib64/ld-2.12.so</span>
0000003ec5221000 4K rw<span style="color:#808080">--- [ anon ]</span>
0000003ec5800000 1628K r-x<span style="color:#808080">-- /lib64/libc-2.12.so</span>
...
0000003ec5b9c000 20K rw<span style="color:#808080">--- [ anon ]</span>
00007f331b910000 96836K r<span style="color:#808080">---- /usr/lib/locale/locale-archive</span>
00007f33217a1000 48K r-x<span style="color:#808080">-- /lib64/libnss_files-2.12.so</span>
...
00007f33219af000 12K rw<span style="color:#808080">--- [ anon ]</span>
00007f33219bf000 8K rw<span style="color:#808080">--- [ anon ]</span>
00007f33219c1000 28K r<span style="color:#808080">--s- /usr/lib64/gconv/gconv-modules.cache</span>
00007f33219c8000 4K rw<span style="color:#808080">--- [ anon ]</span>
00007fff5e553000 84K rw<span style="color:#808080">--- [ stack ]</span>
00007fff5e5e4000 4K r-x<span style="color:#808080">-- [ anon ]</span>
ffffffffff600000 4K r-x<span style="color:#808080">-- [ anon ]</span>
total 108720K</code></span>
多出的171M正是共享库重复计算的部分。
但是由于每个进程共享的东西都不一样,我们也没法知道每个进程是如何共享的,没法做到准确的区分。
总结:内存方面的概念很多,需要深入挖掘!
作者:于峰
链接:http://blog.yufeng.info/archives/245