
作者:Yu Feng
連結:http://blog.yufeng.info/archives/245
前幾天小馬同學問了一個問題:
我ps aux看到的RSS記憶體隻有不到30M,但是free看到記憶體卻已經使用了7,8G了,已經開始swap了,請問ps aux的實際實體記憶體統計是不是漏了哪些記憶體沒算?我有什麼辦法确定free中used的記憶體都去哪兒了呢?
這個問題不止一個同學遇到過了,之前小王同學也遇到這個問題,記憶體的計算總是一個迷糊賬。我們今天來把它算個清楚下!
通常我們是這樣看記憶體的剩餘情況的:
$free -m
total used free shared buffers cached
Mem: 48262 7913 40349 0 14 267
-/+ buffers/cache: 7631 40631
Swap: 2047 336 1711
複制
那麼這個資訊是如何解讀的呢,以下這個圖解釋的挺清楚的!
上面的情況下我們總的記憶體有48262M,用掉了7913M。其中buffer+cache總共14+267=281M, 由于這種類型的記憶體是可以回收的,雖然我們用掉了7913M,但是實際上我們如果實在需要的話,這部分buffer/cache記憶體是可以放出來的。
我們來示範下:
$ sudo sysctl vm.drop_caches=3
vm.drop_caches = 3
$ free -m
total used free shared buffers cached
Mem: 48262 7676 40586 0 3 41
-/+ buffers/cache: 7631 40631
Swap: 2047 336 1711
複制
我們把buffer/cache大部分都清除幹淨了,隻用了44M,是以我們這次used的空間是7676M。
到現在我們比較清楚幾個概念:
1. 總的記憶體多少
2. buffer/cache記憶體可以釋放的。
3. used的記憶體的機率。
即使是這樣我們還是要繼續追查下used的空間(7637M)到底用到哪裡去了?
這裡首先我們來介紹下nmon這個工具,它對記憶體的使用顯示比較直覺。
使用的記憶體的去向我們很自然的就想到作業系統系統上的各種程序需要消耗各種記憶體,我們透過top工具來看下:
通常我們會看程序的RES這一項,這項到底是什麼意思呢?這個數字從哪裡出來的呢?通過strace對top和nmon的追蹤和結合源碼,我們确定這個值是從/proc/PID/statm的第二個字段讀取出來的.
那這個字段什麼意思呢?
man proc
或者http://www.kernel.org/doc/man-pages/online/pages/man5/proc.5.html 會詳細的解釋/proc/下的檔案的具體意思,我們摘抄下:
/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 set size
(same as VmRSS in /proc/[pid]/status)
share shared pages (from shared mappings)
text text (code)
lib library (unused in Linux 2.6)
data data + stack
dt dirty pages (unused in Linux 2.6)
複制
resident set size 也就是每個程序用了具體的多少頁的記憶體。由于linux系統采用的是虛拟記憶體,程序的代碼,庫,堆和棧使用的記憶體都會消耗記憶體,但是申請出來的記憶體,隻要沒真正touch過,是不算的,因為沒有真正為之配置設定實體頁面。
我們實際程序使用的實體頁面應該用resident set size來算的,周遊所有的程序,就可以知道所有的所有的程序使用的記憶體。
我們來實驗下RSS的使用情況:
$ cat RSS.sh
#/bin/bash
for PROC in `ls /proc/|grep "^[0-9]"`
do
if [ -f /proc/$PROC/statm ]; then
TEP=`cat /proc/$PROC/statm | awk '{print ($2)}'`
RSS=`expr $RSS + $TEP`
fi
done
RSS=`expr $RSS \* 4`
echo $RSS"KB"
$ ./RSS.sh
7024692KB
複制
從數字來看,我們的程序使用了大概7024M記憶體,距離7637M還有幾百M記憶體哪裡去了?哪裡去了?貓吃掉了?
我們再回頭來仔細看下nmon的記憶體統計表。
那個該死的slab是什麼呢?那個PageTables又是什麼呢?
簡單的說核心為了高性能每個需要重複使用的對象都會有個池,這個slab池會cache大量常用的對象,是以會消耗大量的記憶體。運作指令:
$ slabtop
複制
我們可以看到:
從圖我們可以看出各種對象的大小和數目,遺憾的是沒有告訴我們slab消耗了多少記憶體。
我們自己來算下好了:
$ echo `cat /proc/slabinfo |awk 'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'` MB
904.256 MB
複制
好吧,把每個對象的數目*大小,再累加,我們就得到了總的記憶體消耗量:904M
那麼PageTables呢?我們萬能的核心組的同學現身了:
伯瑜:
你還沒有計算page tables的大小,還有struct page也有一定的大小(每個頁一個,64bytes),如果是2.6.32的話,每個頁還有一個page_cgroup(32bytes),也就是說記憶體大小的2.3%(96/4096)會被核心固定使用的
含黛:
struct page是系統boot的時候就會根據記憶體大小算出來配置設定出去的,18核心是1.56%左右,32核心由于cgroup的原因會在2.3%
複制
好吧,知道是幹嘛的啦,管理這些實體頁面的硬開銷,那麼具體是多少呢?
$ echo `grep PageTables /proc/meminfo | awk '{print $2}'` KB
58052 KB
複制
好吧,小結下!記憶體的去向主要有3個:
- 1. 程序消耗。
- 2. slab消耗
- 3.pagetable消耗。
我把三種消耗彙總下和free出的結果比對下,這個腳本的各種計算項仲同學幫忙搞定的:
$ cat cm.sh
#/bin/bash
for PROC in `ls /proc/|grep "^[0-9]"`
do
if [ -f /proc/$PROC/statm ]; then
TEP=`cat /proc/$PROC/statm | awk '{print ($2)}'`
RSS=`expr $RSS + $TEP`
fi
done
RSS=`expr $RSS \* 4`
PageTable=`grep PageTables /proc/meminfo | awk '{print $2}'`
SlabInfo=`cat /proc/slabinfo |awk 'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'`
echo $RSS"KB", $PageTable"KB", $SlabInfo"MB"
printf "rss+pagetable+slabinfo=%sMB\n" `echo $RSS/1024 + $PageTable/1024 + $SlabInfo|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
複制
free報告說7629, 我們的cm腳本報告說7800.3M, 我們的CM多報了171M。
damn,這又怎麼回事呢?
複制
我們重新校對下我們的計算。我們和nmon來比對下,slab和pagetable的值是吻合的。那最大的問題可能在程序的消耗計算上。
resident resident set size 包括我們使用的各種庫和so等共享的子產品,在前面的計算中我們重複計算了。
$ pmap `pgrep bash`
...
22923: -bash
0000000000400000 848K r-x-- /bin/bash
00000000006d3000 40K rw--- /bin/bash
00000000006dd000 20K rw--- [ anon ]
00000000008dc000 36K rw--- /bin/bash
00000000013c8000 592K rw--- [ anon ]
000000335c400000 116K r-x-- /lib64/libtinfo.so.5.7
...
0000003ec5220000 4K rw--- /lib64/ld-2.12.so
0000003ec5221000 4K rw--- [ anon ]
0000003ec5800000 1628K r-x-- /lib64/libc-2.12.so
...
0000003ec5b9c000 20K rw--- [ anon ]
00007f331b910000 96836K r---- /usr/lib/locale/locale-archive
00007f33217a1000 48K r-x-- /lib64/libnss_files-2.12.so
...
00007f33219af000 12K rw--- [ anon ]
00007f33219bf000 8K rw--- [ anon ]
00007f33219c1000 28K r--s- /usr/lib64/gconv/gconv-modules.cache
00007f33219c8000 4K rw--- [ anon ]
00007fff5e553000 84K rw--- [ stack ]
00007fff5e5e4000 4K r-x-- [ anon ]
ffffffffff600000 4K r-x-- [ anon ]
total 108720K
複制
多出的171M正是共享庫重複計算的部分。
但是由于每個程序共享的東西都不一樣,我們也沒法知道每個程序是如何共享的,沒法做到準确的區分。
總結:記憶體方面的概念很多,需要深入挖掘!
是以隻能留點小遺憾,歡迎大家留言積極參與探讨。