最近在維護一臺CentOS服務(wù)器的時候,發(fā)現(xiàn)內(nèi)存無端"損失"了許多,free和ps統(tǒng)計的結(jié)果相差十幾個G,搞的我一度又以為遇到靈異事件了,后來Google了許久才搞明白,特此記錄一下,以供日后查詢。
雖然天天都在用Linux系統(tǒng)辦公,其實對它的了解也不過爾爾。畢業(yè)幾年才邁入"知道自己不知道"的境界,我覺得自己絲毫沒有愧對萬年吊車尾這個稱號 :(
同事說有一臺服務(wù)器的內(nèi)存用光了,我連上去用free看了下,確實有點怪。
$ free -g total used free shared buffers cached Mem: 15 15 0 0 2 0 /+ buffers/cache: 12 2 Swap: 17 0 17
這臺服務(wù)器有16G內(nèi)存,但是結(jié)果顯示除了2G左右的文件Buffer緩存外,其余十幾G都被確確實實的用光了。(free按1024進制計算,總內(nèi)存可能比實際偏小)
這里大概介紹下free結(jié)果的含義:
/ | total | used | free | shared | buffers | cached |
---|---|---|---|---|---|---|
Mem | 總物理內(nèi)存 | 當前使用的內(nèi)存(包括slab+buffers+cached) | 完全沒有使用的內(nèi)存 | 進程間共享的內(nèi)存 | 緩存文件的元數(shù)據(jù)1 | 緩存文件的具體內(nèi)容1 |
/+ buffers/cache | 當前使用的內(nèi)存(不包括buffers+cached,但包括slab) | 未使用和緩存的內(nèi)存(free+buffers+cached) | ||||
Swap | 總的交換空間 | 已使用的交換空間 | 未使用的交換空間 |
然后top看了下,沒有特別吃內(nèi)存的程序。用ps大概統(tǒng)計下所有程序占用的總內(nèi)存:
$ ps aux | awk '{mem += $6} END {print mem/1024/1024}' 0.595089
結(jié)果顯示所有進程占用的內(nèi)存還不到1G,實際上,因為free, ps的統(tǒng)計方式的差別和Copy-on-write和Shared libraries等內(nèi)存優(yōu)化機制的存在,這兩者的統(tǒng)計結(jié)果通常是不一樣的。但是一般情況下**不會相差十幾個G,肯定是有什么隱藏的問題,Google了許久后發(fā)現(xiàn),free沒有專門統(tǒng)計另一項緩存: Slab。
Slab Allocation是Linux 2.2之后引入的一個內(nèi)存管理機制,專門用于緩存內(nèi)核的數(shù)據(jù)對象,可以理解為一個內(nèi)核專用的對象池,可以提高系統(tǒng)性能并減少內(nèi)存碎片。(Linux 2.6.23之后,SLUB成為了默認的allocator。)
查看Slab緩存
$ cat /proc/meminfo
其中,Slab相關(guān)的數(shù)據(jù)為
Slab: 154212 kB SReclaimable: 87980 kB SUnreclaim: 66232 kB
SReclaimable(Linux 2.6.19+)都是clean的緩存,隨時可以釋放?;氐街暗膬?nèi)存問題,我查了下那臺服務(wù)器上Slab占用的內(nèi)存:
$ cat /proc/meminfo | grep Slab Slab: 12777668 kB
12G的Slab緩存,有意思的是free把Slab緩存統(tǒng)計到了used memory中,這就是之前那個問題的癥結(jié)所在了。
另外,還可以查看/proc/slabinfo(或使用slabtop命令)來查看Slab緩存的具體使用情況。結(jié)果發(fā)現(xiàn),ext3_inode_cache和dentry_cache占用了絕大部分內(nèi)存。
考慮到這臺服務(wù)器會頻繁地用rsync同步大量的文件,這個結(jié)果也并不意外。
先說明一下,如果問題僅僅是Slab占用了太多的內(nèi)存(SReclaimable),那么通常不需要太操心,因為這根本不是個問題(如果是 SUnreclaim太多且不斷增長,那么很有可能是內(nèi)核有bug)。但是,如果是因為Slab占用內(nèi)存太多而引起了其他的問題,建議繼續(xù)閱讀。
清除Slab可回收緩存
通過/proc/sys/vm/drop_caches這個配置項,我們可以手動清除指定的可回收緩存(SReclaimable)2。
echo 2 > /proc/sys/vm/drop_caches
上面的命令會主動釋放Slab中clean的緩存(包括inode和dentry的緩存),然后再free -g一下,未使用的內(nèi)存陡增了十幾個G。。。
需要注意的是,手動清除緩存可能會在一段時間內(nèi)降低系統(tǒng)性能。原則上不推薦這么做,因為如果有需要,系統(tǒng)會自動釋放出內(nèi)存供其他程序使用。
另外,手動清除Slab緩存是一個治標不治本的辦法。因為問題不在Slab,而在于我們那個會引起Slab緩存飆漲的進程(我這里應(yīng)該是 rsync)。實際操作的時候發(fā)現(xiàn),清除緩存一段時間后,Slab緩存很快又會“反彈”回去。如果需要治本,要么搞定問題進程,要么修改系統(tǒng)配置。
調(diào)整系統(tǒng)vm配置
風(fēng)險預(yù)警: 調(diào)整以下系統(tǒng)配置可能會對系統(tǒng)性能造成負面影響,請仔細測試并謹慎操作。
/etc/sysctl.conf里有幾個對內(nèi)存管理影響比較大的配置,以下配置項的文檔見vm.txt。
vm.vfs_cache_pressure
系統(tǒng)在進行內(nèi)存回收時,會先回收page cache, inode cache, dentry cache和swap cache。vfs_cache_pressure越大,每次回收時,inode cache和dentry cache所占比例越大3。
vfs_cache_pressure默認是100,值越大inode cache和dentry cache的回收速度會越快,越小則回收越慢,為0的時候完全不回收(OOM!)。
圖片取自The Linux Kernel's VFS Layer
vm.min_free_kbytes
系統(tǒng)的"保留內(nèi)存"的大小,"保留內(nèi)存"用于低內(nèi)存狀態(tài)下的"atomic memory allocation requests"(eg. kmalloc + GFP_ATOMIC),該參數(shù)也被用于計算開始內(nèi)存回收的閥值,默認在開機的時候根據(jù)當前的內(nèi)存計算所得,越大則表示系統(tǒng)會越早開始內(nèi)存回收。
min_free_kbytes過大可能會導(dǎo)致OOM,太小可能會導(dǎo)致系統(tǒng)出現(xiàn)死鎖等問題。
vm.swappiness
該配置用于控制系統(tǒng)將內(nèi)存swap out到交換空間的積極性,取值范圍是[0, 100]。swappiness越大,系統(tǒng)的交換積極性越高,默認是60,如果為0則不會進行交換。
man proc
The Linux Kernel's VFS Layer
The VFS in Linux Kernel V2.4
openSUSE: System Analysis and Tuning Guide, Chapter 15. Tuning the Memory Management Subsystem
Red Hat Enterprise Linux, 5.5 Tuning Virtual Memory
Odd behavior
Wikipedia:Slab allocation
Linux System IO Monitoring
Paging
Understanding the Linux Virtual Memory Manager
Understanding the Linux Kernel, 3rd Edition
1 2
Red Hat Enterprise Linux,5.1 Checking the Memory Usage,引用于2014-12-12。
Linux kernel documentation, sysctl#vm,引用于2014-12-12。
@TODO: 需要查看相關(guān)內(nèi)核代碼來確認,不過看vm.txt里的描述應(yīng)該是這個意思。