天天看點

Linux Used記憶體到底哪裡去了?作者:Yu Feng 連結:http://blog.yufeng.info/archives/245

Linux Used記憶體到底哪裡去了?作者:Yu Feng 連結:http://blog.yufeng.info/archives/245

作者: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           

複制

那麼這個資訊是如何解讀的呢,以下這個圖解釋的挺清楚的!

Linux Used記憶體到底哪裡去了?作者:Yu Feng 連結:http://blog.yufeng.info/archives/245

上面的情況下我們總的記憶體有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這個工具,它對記憶體的使用顯示比較直覺。

Linux Used記憶體到底哪裡去了?作者:Yu Feng 連結:http://blog.yufeng.info/archives/245

使用的記憶體的去向我們很自然的就想到作業系統系統上的各種程序需要消耗各種記憶體,我們透過top工具來看下:

Linux Used記憶體到底哪裡去了?作者:Yu Feng 連結:http://blog.yufeng.info/archives/245

通常我們會看程序的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的記憶體統計表。

Linux Used記憶體到底哪裡去了?作者:Yu Feng 連結:http://blog.yufeng.info/archives/245

那個該死的slab是什麼呢?那個PageTables又是什麼呢?

簡單的說核心為了高性能每個需要重複使用的對象都會有個池,這個slab池會cache大量常用的對象,是以會消耗大量的記憶體。運作指令:

$ slabtop           

複制

我們可以看到:

Linux Used記憶體到底哪裡去了?作者:Yu Feng 連結:http://blog.yufeng.info/archives/245

從圖我們可以看出各種對象的大小和數目,遺憾的是沒有告訴我們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正是共享庫重複計算的部分。

但是由于每個程序共享的東西都不一樣,我們也沒法知道每個程序是如何共享的,沒法做到準确的區分。

總結:記憶體方面的概念很多,需要深入挖掘!

是以隻能留點小遺憾,歡迎大家留言積極參與探讨。