有朋友發現有臺128的內存結果發現在使用時有點不對了,下面小編整理了一篇關于linux內存去哪兒了問題解決辦法吧.
前兩天一臺128G內存的oracle主機發生故障觸發kdump,最終由于var目錄空間不足,導致kdump生成不完全,結合之前redhat給出的建議,crash設置的空間最好大于memory 空間,對此我們做了一個簡單的計算,認為kdump主機生成的是運行在內存里的信息,雖然主機有128G的內存,不過通過top查看并計算后發現我實際上只使用7G多的大小,而使用free -m查看時已經使用了80G左右的內存,站在DBA的角度看的話,這部分內存提前分配給了sga,貌似也可以講通,記得之前看過taobao褚霸寫的一篇分析,這里再結合該文章算算.
通過褚霸的Linux Used內存到底哪里去了?我們已經了解到內存主要消耗在三個方面:
1.進程消耗
2.slab消耗
3.pagetable消耗
由于不便于直接在現網oracle主機上進行操作,這里就以本blog的云主機為例進行測試.
一、查看已用內存總量
- [root@91it ~]# free -m
- total used free shared buffers cached
- Mem: 996 908 88 0 174 283
- -/+ buffers/cache: 450 546
- Swap: 0 0 0
于已用內存和可用內存這已經是一個老生長談的問題了,這里看到的信息如下.
1、總內存996M,已用內存908M
2、由于buffers + cached內存實際上也是可用內存,該內存也可以通過echo 3 > /proc/sys/vm/drop_caches 回收pagechae、dentries and inodes,所以實際上已經使用的內存是450M.
注:1、關于內存的計算方法就不上圖了,這點可以參考:http://www.redbooks.ibm.com/redpapers/pdfs/redp4285.pdf
2、linux內存強制回收的方法具體可以參考:linux 下 強制回收內存.
即然實際使用了450M內存,那這450M內存是如何分配的呢?
二、RSS內存(Resident size)
ps下命令下的RSS內存、top工具下的RES內存都是指的這一塊內存,resident set size 也就是每個進程用了具體的多少頁的內存,由于linux系統采用的是虛擬內存,進程的代碼,庫,堆和棧使用的內存都會消耗內存,但是申請出來的內存,只要沒真正touch過,是不算的,因為沒有真正為之分配物理頁面,說白了也就是真正具有“數據頁的物理內存”,我這里使用的是一段從python psutil 模塊里演化出的一段代碼進行計算的:
- [root@91it ~]# cat vms.py
- #!/usr/bin/python
- import os
- def get_vms(pid):
- with open("/proc/%s/statm" % pid, "rb") as f:
- vms = f.readline().split()[1]
- return int(vms)
- pids = [int(x) for x in os.listdir(b'/proc') if x.isdigit()]
- vmss = [get_vms(pid) for pid in pids]
- print sum(vmss) * 4
- [root@91it ~]# python vms.py
- 386528
注:1、/proc/PID/statm 第二列是RSS內存使用page頁的多少,而在linux下默認使用的page頁大小是4KB,所以我上面計算求和后,最后乘以4,而我最終計算出的結果就是386528KB.
2、這里也可以通過/proc/PID/status里的vmRss項進行求和,因為該項直接給出的是KB值.
- [root@91it ~]# cat /proc/998/status
- Name: mingetty
- State: S (sleeping)
- Tgid: 998
- Pid: 998
- PPid: 1
- TracerPid: 0
- Uid: 0 0 0 0
- Gid: 0 0 0 0
- Utrace: 0
- FDSize: 64
- Groups:
- VmPeak: 4068 kB
- VmSize: 4064 kB
- VmLck: 0 kB
- VmHWM: 556 kB
- VmRSS: 76 kB
- ………………………………
當然也可以使用shell 進行計算:
- $ 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內存部分,具體可以查看man proc手冊或kernl 頁的介紹,以下是man 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)
二、slab內存
slab內存的作用是內核為了高性能每個需要重復使用的對象都會有個池,這個slab池會cache大量常用的對象,所以會消耗大量的內存,具體可以運行slabtop命令查看.
slab內存的消耗我們可以通過/proc/slabinfo文件算出,具腳本為:
- # echo `cat /proc/slabinfo |awk 'BEGIN{sum=0;}{sum=sum+$3*$4;}END{print sum/1024/1024}'` MB
- 74.7215 MB
三、PageTables內存
這部分內存我并沒有細研究,這里就直接拉taobao上各位大牛的說法用吧:“struct page也有一定的大小(每個頁一個,64bytes),如果是2.6.32的話,每個頁還有一個page_cgroup(32bytes),也就是說內存大小的2.3%(96/4096)會被內核固定使用,struct page是系統boot的時候就會根據內存大小算出來分配出去的,18內核是1.56%左右,32內核由于cgroup的原因會在2.3%.”
而具體消耗可以通過/proc/meminfo里的pagetables項獲取,腳本如下:
- # echo `grep PageTables /proc/meminfo | awk '{print $2}'` KB
- 4476 KB
系統的硬開銷占比并不多.
四、算算總帳
三者加起來發現要大于450M,這里我們便于查看,再跑下腳本:
- $ 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=%sMBn" `echo $RSS/1024 + $PageTable/1024 + $SlabInfo|bc` //Vevb.com
- free -m
- $ ./cm.sh
- 382048KB, 4528KB, 74.8561MB
- rss+pagetable+slabinfo=451.8561MB
- total used free shared buffers cached
- Mem: 996 842 154 0 106 376
- -/+ buffers/cache: 359 637
- Swap: 0 0 0
由于上面演示時,我強制回收過內存,目前實際已用內存為359M,而我們上面三者之和是451M,比實際使用的大了100M左右.
多出的這部分內存是我們在計算rss內存時算重復了,因為rss內存包括我們使用的各種庫和so等共享的模塊,具體可以使用pmap指令查看詳細的每個進程調用的lib庫及已用內存值,這里以最簡單的bash進程為例.
- [root@91it ~]# pmap `pgrep bash`
- 1464: -bash
- 0000000000400000 848K r-x-- /bin/bash
- 00000000006d3000 40K rw--- /bin/bash
- 00000000006dd000 20K rw--- [ anon ]
- 00000000008dc000 36K rw--- /bin/bash
- 00000000011a4000 396K rw--- [ anon ]
- 0000003ef9800000 128K r-x-- /lib64/ld-2.12.so
- 0000003ef9a1f000 4K r---- /lib64/ld-2.12.so
- 0000003ef9a20000 4K rw--- /lib64/ld-2.12.so
- 0000003ef9a21000 4K rw--- [ anon ]
- 0000003ef9c00000 1576K r-x-- /lib64/libc-2.12.so
- 0000003ef9d8a000 2048K ----- /lib64/libc-2.12.so
- 0000003ef9f8a000 16K r---- /lib64/libc-2.12.so
- 0000003ef9f8e000 4K rw--- /lib64/libc-2.12.so
- 0000003ef9f8f000 20K rw--- [ anon ]
- 0000003efa400000 8K r-x-- /lib64/libdl-2.12.so
- 0000003efa402000 2048K ----- /lib64/libdl-2.12.so
- 0000003efa602000 4K r---- /lib64/libdl-2.12.so
- 0000003efa603000 4K rw--- /lib64/libdl-2.12.so
- 0000003efb800000 116K r-x-- /lib64/libtinfo.so.5.7
- 0000003efb81d000 2048K ----- /lib64/libtinfo.so.5.7
- 0000003efba1d000 16K rw--- /lib64/libtinfo.so.5.7
- 00007f1a42fc8000 96836K r---- /usr/lib/locale/locale-archive
- 00007f1a48e59000 48K r-x-- /lib64/libnss_files-2.12.so
- 00007f1a48e65000 2048K ----- /lib64/libnss_files-2.12.so
- 00007f1a49065000 4K r---- /lib64/libnss_files-2.12.so
- 00007f1a49066000 4K rw--- /lib64/libnss_files-2.12.so
- 00007f1a49067000 12K rw--- [ anon ]
- 00007f1a4906b000 8K rw--- [ anon ]
- 00007f1a4906d000 28K r--s- /usr/lib64/gconv/gconv-modules.cache
- 00007f1a49074000 4K rw--- [ anon ]
- 00007fff48189000 84K rw--- [ stack ]
- 00007fff481ff000 4K r-x-- [ anon ]
- ffffffffff600000 4K r-x-- [ anon ]
- total 108472K
- 2757: -bash
- 0000000000400000 848K r-x-- /bin/bash
- 00000000006d3000 40K rw--- /bin/bash
- 00000000006dd000 20K rw--- [ anon ]
- 00000000008dc000 36K rw--- /bin/bash
- 0000000001385000 396K rw--- [ anon ]
- 0000003ef9800000 128K r-x-- /lib64/ld-2.12.so
- 0000003ef9a1f000 4K r---- /lib64/ld-2.12.so
- 0000003ef9a20000 4K rw--- /lib64/ld-2.12.so
- 0000003ef9a21000 4K rw--- [ anon ]
- 0000003ef9c00000 1576K r-x-- /lib64/libc-2.12.so
- 0000003ef9d8a000 2048K ----- /lib64/libc-2.12.so
- 0000003ef9f8a000 16K r---- /lib64/libc-2.12.so
- 0000003ef9f8e000 4K rw--- /lib64/libc-2.12.so
- 0000003ef9f8f000 20K rw--- [ anon ]
- 0000003efa400000 8K r-x-- /lib64/libdl-2.12.so
- 0000003efa402000 2048K ----- /lib64/libdl-2.12.so
- 0000003efa602000 4K r---- /lib64/libdl-2.12.so
- 0000003efa603000 4K rw--- /lib64/libdl-2.12.so
- 0000003efb800000 116K r-x-- /lib64/libtinfo.so.5.7
- 0000003efb81d000 2048K ----- /lib64/libtinfo.so.5.7
- 0000003efba1d000 16K rw--- /lib64/libtinfo.so.5.7
- 00007fda04cb1000 96836K r---- /usr/lib/locale/locale-archive
- 00007fda0ab42000 48K r-x-- /lib64/libnss_files-2.12.so
- 00007fda0ab4e000 2048K ----- /lib64/libnss_files-2.12.so
- 00007fda0ad4e000 4K r---- /lib64/libnss_files-2.12.so
- 00007fda0ad4f000 4K rw--- /lib64/libnss_files-2.12.so
- 00007fda0ad50000 12K rw--- [ anon ]
- 00007fda0ad54000 8K rw--- [ anon ]
- 00007fda0ad56000 28K r--s- /usr/lib64/gconv/gconv-modules.cache
- 00007fda0ad5d000 4K rw--- [ anon ]
- 00007fff0e9e0000 84K rw--- [ stack ]
- 00007fff0e9ff000 4K r-x-- [ anon ]
- ffffffffff600000 4K r-x-- [ anon ]
- total 108472K
從上面的指令上看出,pid為1464和2757兩個進程使用很多相同的so文件,而且其內存地址也相同,這里個人認為rss部分的內存理論上是可以完全計算準確的,做法就是先遍歷/proc下所有的pid,再pmap所有pid,對所有的輸出結果進行匯總后去重,再將第二列的占用內存值求和,具體也可以考慮從/proc/pid/smaps文件入手.
五、其他情況
在第四步算算總帳中,我們看出 rrs內存 + slab內存 + pagetables內存 > 實際已用內存,但該情況并非是絕對的,也有例外,在我上面提到的oracle 使用場景下,事先分配了70g左右的oracle sga 空間(即內存空間),而三者相加要遠遠小于實際使用的物理內存,這又要怎么解釋呢?再去除cache和buffer的空間也要遠遠小于實際使用的內存.
- [root@irora04s ~]# free -m
- total used free shared buffers cached
- Mem: 129023 100024 28999 0 885 12595
- -/+ buffers/cache: 86543 42480
- Swap: 24575 0 24575
- [root@irora04s ~]# sh /tmp/mem.sh
- 4339696KB, 66056KB, 745.805MB
- rss+pagetable+slabinfo=5046.805MB
- total used free shared buffers cached
- Mem: 129023 100096 28926 0 885 12597
- -/+ buffers/cache: 86614 42409
- Swap: 24575 0 24575
我個人的理解是事先分配的這部分sga內存,大部分是空page頁,在未使用時雖然空間被占用了,但該內存地址內并不存在數據,所以一旦該機觸發kdump 時,crash 的內存空間占用的磁盤空間 接近于rss+pagetable+slabinfo,小于rss+pagetable+slabinfo+buffers+cached.
最后這里同樣推薦有時間看下nmon的源碼,因為從nmon的內存統計信息來看,更便于理解內存的幾個去向.
新聞熱點
疑難解答