free和top显示可用内存不一致
文章目錄
- 1. 問題說明
- 2. 折騰之路
- 2.1 Slab簡介
- 2.1.1 Slab有兩個主要作用:
- 2.1.2 Slab的信息記錄
- 2.2 dentry 簡介
- 2.2.1 dentry清空方案
- 2.3 SystemTap簡介
- 2.3.1 SystemTap 安裝
- 2.4. 使用systemTap來監(jiān)控對dentry的使用
- 2.4.1 從用戶進程調用上來統(tǒng)計
- 2.4.2 curl請求bug的發(fā)現
- 3 . 數據樣例附
- 參考
1. 問題說明
??在國慶的最后一天夜里,服務器突然報警,爆的是內存不夠使用了,剩余不到5%。因為該服務器上暫時只運行了一個logstash節(jié)點,服務器內存是31g,logstash的jvm堆的大小設置的是24g??吹綀缶谝环磻?#xff0c;我靠,又發(fā)生堆外內存泄漏了?上了服務器,使用top一看,對應的logstash確實只使用24g,對應的其他進程的消耗都很小,加起來不過25g的樣子,但是使用free -h一看,卻是已經使用了比較高的內存。顯示只有1g的緩存了,對應top的話有5g左右的差距,系統(tǒng)按說不應該占用這么多啊。沒辦法,先解決報警吧。就臨時將logstash的jvm內存調整到22g,然后重啟。后面再慢慢的排查這個問題。
下面的排查數據都是重啟后的,但是依然可以說明問題
1.使用top,free查看相關信息
這里從RES可以看到用戶進程最多消耗的內存是19g+306m 可以認為不到20g
我們再來看看free命令的情況
可以看到從用戶角度看到的進程使用的總內存是24G,這里面有4G左右的偏差,理論上系統(tǒng)不可能占用這么多,而且也會再top中顯示的啊。
2.系統(tǒng)內核
cat /etc/issue CentOS release 6.8 (Final)cat /proc/version Linux version 2.6.32-642.6.2.el6.x86_64 (mockbuild@worker1.bsys.centos.org) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC) ) #1 SMP Wed Oct 26 06:52:09 UTC 20162. 折騰之路
這4g的內存到底去了哪里了呢,不知道的話心里總是很癢,難受。首先,這里的表現是top和free顯示不一致,想要搞明白這倆命令的統(tǒng)計原理有點費勁,google一下,沒有找到。后來同事幫忙用free可用內存偏少關鍵字找到了這樣一篇文章,這才解開了這個問題的面紗。
原來是free相對top沒有專門統(tǒng)計Slab。這也是導致兩者差別的主要原因。
這個時候想到可以看看內存的具體的數據對照一下
可以使用 cat /proc/meminfo看到內存的更具體的使用情況
2.1 Slab簡介
Slab是內核中使用的數據結構,為了適應更小的數據分配(比頁小很多,頁通常是4k)。
更加具體的可以參看這里
2.1.1 Slab有兩個主要作用:
- Slab對小對象進行分配,不用為每個小對象分配一個頁,節(jié)省了空間。
- 內核中一些小對象創(chuàng)建析構很頻繁,Slab對這些小對象做緩存,可以重復利用一些相同的對象,減少內存分配次數。
2.1.2 Slab的信息記錄
** 1.Slab的統(tǒng)計信息 **
統(tǒng)計信息記錄在/proc/meminfo中對應的項是
Slab,SReclaimable,SUnreclaim
其中Slab=SReclaimable+SUnreclaim,SReclaimable表示可回收使用的內存。
** 2. Slab 詳情 **
Slab詳細包含的項存儲在/proc/slabinfo當中
這里主要介紹一下前幾個列的含義,如上中文注釋,這個文件顯示了slab使用的詳細分布。這里可以看到dentry 占用的是最大的,他占用的內存可以這樣計算
20061020*192/1024=3851727kb 可以得到對應的kb size 。
這個文件還有一個更簡單的命令可以看:slabtop
這里可以看到各個維度的總結性信息以及詳情,而且自動排序了。同樣也是dentry占用最多,但是對應的是cache-size和上面計算出來的不一樣,不清楚是不是因為slab是從頁中分配出來的所以導致頁有空閑導致的。
slabtop -o|head -20Active / Total Objects (% used) : 21372898 / 21401792 (99.9%)Active / Total Slabs (% used) : 1062385 / 1062554 (100.0%)Active / Total Caches (% used) : 99 / 177 (55.9%)Active / Total Size (% used) : 3982075.22K / 3995162.08K (99.7%)Minimum / Average / Maximum Object : 0.02K / 0.19K / 4096.00KOBJS ACTIVE USE OBJ-SIZE SLABS OBJ/SLAB CACHE-SIZE NAME 20061120 20060914 99% 0.19K 1003056 20 4012224K dentry 1054685 1054592 99% 0.10K 28505 37 114020K buffer_head58550 58528 99% 0.78K 11710 5 46840K ext3_inode_cache51271 48907 95% 0.06K 869 59 3476K size-6443218 43152 99% 0.55K 6174 7 24696K radix_tree_node20140 11226 55% 0.98K 5035 4 20140K ext4_inode_cache19488 19057 97% 0.03K 174 112 696K size-3210080 10037 99% 0.12K 315 32 1260K inotify_inode_mark_entry9633 7502 77% 0.20K 507 19 2028K vm_area_struct9288 9280 99% 0.14K 344 27 1376K sysfs_dir_cache8701 6437 73% 0.05K 113 77 452K anon_vma_chain7844 7610 97% 0.07K 148 53 592K selinux_inode_security6114 5955 97% 0.64K 1019 6 4076K proc_inode_cache可以看到,這里的dentry 是最多的,那么dentry又是什么東西呢?
2.2 dentry 簡介
dentry (directory entry),目錄項緩存。也就是內核中用來作為目錄的索引,和inode 共同標識了文件系統(tǒng),inode 存儲的是文件的元信息,dentry存儲的是文件名加目錄的信息,為了快速定位文件。
所以在文件特別多的情況下有可能會導致dentry量比較大。具體介紹參看這里
在文件比較多或者頻繁刪除創(chuàng)建文件的時候可能會導致dentry比較多。這個是最直接的原因。如何查看系統(tǒng)的文件,inode數量。
inodes的數量就代表了dentry的最大數量,可以看到3276800和dentry的數量完全不在一個數量級,所以肯定不是因為系統(tǒng)中的文件太多導致。
但是具體是哪些進程使用了dentry有些難以判斷,系統(tǒng)沒有直接的信息進行記錄。這個時候需要使用大殺器 SystemTap了。
2.2.1 dentry清空方案
系統(tǒng)默認內存回收配置在/proc/sys/vm/drop_caches中
0:不做任何處理,由系統(tǒng)自己管理 1:清空pagecache 2:清空dentries和inodes 3:清空pagecache、dentries和inodes所以如果想清空dentry,只需要
echo "2" > /proc/sys/vm/drop_caches等待slab有效下降以后,再恢復 echo "0" > /proc/sys/vm/drop_caches2.3 SystemTap簡介
SystemTap是一個Linux非常有用的調試(跟蹤/探測)工具,常用于Linux
內核或者應用程序的信息采集,比如:獲取一個函數里面運行時的變
量、調用堆棧,甚至可以直接修改變量的值,對診斷性能或功能問題非
常有幫助。SystemTap提供非常簡單的命令行接口和很簡潔的腳本語
言,以及非常豐富的tapset和例子。 類似于java的btrace一樣,可以用來調試c的調用等。
這個玩意兒太強大了,只是我用的還不是很熟悉,后面有機會了需要繼續(xù)學習。簡單介紹一下安裝吧。
2.3.1 SystemTap 安裝
安裝步驟不算太難,但是網上說的亂七八糟的,很坑,總結一下我的吧。
1. 安裝依賴包
SystemTap有一些依賴包,所以要先裝這些依賴包。
依賴包有以下3個
- kernel-debuginfo
- kernel-debuginfo-common
- kernel-devel
這三個包都是和操作系統(tǒng)的內核版本相關的,所以要先查看內核版本
所以對應的三個包分別是
kernel-debuginfo-2.6.32-642.6.2.el6.x86_64.rpm kernel-debuginfo-common-x86_64-2.6.32-642.6.2.el6.x86_64.rpm kernel-devel-2.6.32-642.6.2.el6.x86_64.rpm建議直接google,然后找到對應的ftp服務器wget下載,我這邊也示例以下吧
wget http://ftp.riken.jp/Linux/scientific/6.2/archive/debuginfo/kernel-debuginfo-2.6.32-642.6.2.el6.x86_64.rpm wget http://ftp.riken.jp/Linux/scientific/6.2/archive/debuginfo/kernel-debuginfo-2.6.32-642.6.2.el6.x86_64.rpm wget http://ftp.riken.jp/Linux/scientific/6.2/archive/debuginfo/kernel-debuginfo-common-x86_64-2.6.32-642.6.2.el6.x86_64.rpm然后安裝這三個包
rmp -ivh *.rpm2. yum 安裝SystemTap
接下來就簡單了
為了部署 SystemTap,需要安裝以下兩個 RPM 包:
- systemtap
- systemtap-runtime
2.4. 使用systemTap來監(jiān)控對dentry的使用
2.4.1 從用戶進程調用上來統(tǒng)計
這兩個函數對應的是對dentry的申請和釋放,所以通過對這兩個函數的調用可以看到對應的對dentry的使用情況。
cat name_record.stpprobe kernel.function("d_alloc") {printf("%s[%ld] %s %s\n", execname(), pid(), pp(), probefunc()) } probe kernel.function("d_free") {printf("%s[%ld] %s %s\n", execname(), pid(), pp(), probefunc()) } probe timer.s(600) {exit() }使用命令 stap name_record.stpn > name.log即可獲取相關的信息,name.log中的內容是下面這個樣子的。
vim name.logYDService[26276] kernel.function("d_alloc@fs/dcache.c:968") d_alloc YDService[26276] kernel.function("d_free@fs/dcache.c:89") d_free ... 05.nodes_only_b[10880] kernel.function("d_free@fs/dcache.c:89") d_free 05.nodes_only_b[10531] kernel.function("d_free@fs/dcache.c:89") d_free 05.nodes_only_b[10531] kernel.function("d_alloc@fs/dcache.c:968") d_alloc ... curl[10650] kernel.function("d_alloc@fs/dcache.c:968") d_alloc curl[10650] kernel.function("d_free@fs/dcache.c:89") d_free curl[10650] kernel.function("d_alloc@fs/dcache.c:968") d_alloc curl[10650] kernel.function("d_free@fs/dcache.c:89") d_free可以看到是哪個進程調用了這兩個函數。這兩個函數是進程可以主動進行調用的。
統(tǒng)計一下對應的進程調用
# 總量26w wc -l name.log 269715 name.log# 按照進程排序 awk '{print $1}' name.log|sort |uniq -c|sort -n -k1 |tail -n301563 05.nodes_only_b[1698]1563 05.nodes_only_b[19280]1563 05.nodes_only_b[4655]1563 05.nodes_only_b[7605]1564 05.nodes_only_b[16327]1564 05.nodes_only_b[25307]1644 kk-superman-age[23735]2166 sadc[13116]2299 sadc[16307]2299 sadc[22276]2299 sadc[25301]2299 sadc[4659]2300 sadc[1697]2300 sadc[19281]2300 sadc[28249]2300 sadc[31200]2300 sadc[7594]2320 barad_agent[3897]4250 falcon-agent[21818]5574 curl[27094]6503 curl[15143]6759 curl[30036]6796 curl[516]6860 curl[9376]6956 curl[6437]7024 curl[21094]7055 curl[24071]7063 curl[18113]7148 curl[3494]72149 YDService[26276]可以看到如果按照進程來算的話是YDService 是最多的。先驗證一下這個服務。
$ cat name.log |grep "YD"|grep 'd_free'|wc -l 36467 $ cat name.log |grep "YD"|grep 'd_alloc'|wc -l 36572可以看到這個服務free和alloc的量基本上是一致的,所以沒有啥問題。
然后我們再來校驗一下curl(因為之前看文檔說curl在請求https的時候某寫版本是有問題的,所以將所有的curl請求當做一個進程來處理)
$ cat name.log |grep "curl"|grep 'd_alloc'|wc -l 68294 $ cat name.log |grep "curl"|grep 'd_free'|wc -l 738這一個看,差的好大,估計就是這個了,計算一下按照這個量每分鐘產生的大小。
(68294-738)*192/1024/10=1266kb
然后寫了一個腳本來記錄每分鐘的slab,dentry的變換量
#/bin/bashlog_file="$(cd $(dirname $0);pwd)/log_slab.log" if [ ! -e $log_file ] ; then echo "time slab-kb dentry-kb diff dentry_add" > $log_fileecho "2019.10.09-21:01:19 2929348 2685424 243924" >> $log_file fidentry_size=$( /usr/bin/slabtop -o|grep dentry |awk '{print $7}'|sed -e "s/K//g") slab_size=$( cat /proc/meminfo | grep Slab|awk '{print $2}') cur_time=$(date +'%Y.%m.%d-%H:%M:%S') diff_s_d=$(echo "${slab_size}-${dentry_size}"|bc)lastest_dentry=$(tail -n1 ${log_file}|awk '{print $3}') diff_dentry=$(echo "${dentry_size} - ${lastest_dentry}"|bc)res="${cur_time} ${slab_size} ${dentry_size} ${diff_s_d} ${diff_dentry}" echo "$res" >> ${log_file}然后加到定時任務當中
crontab -e * * * * * bash /home/xx/dentry_record_/record.sh后面可以看到每分鐘的增量大概是
[deploy@bj3-search-log-logstash01 dentry_record_]$ head -30 log_slab.log time slab-kb dentry-kb diff dentry_add 2019.10.09-21:16:01 2950764 2707576 243188 0 2019.10.09-21:17:01 2951940 2708652 243288 1076 2019.10.09-21:18:01 2953528 2710272 243256 1620 2019.10.09-21:19:01 2955564 2711804 243760 1532 2019.10.09-21:20:01 2956880 2713420 243460 1616 2019.10.09-21:21:01 2958904 2715032 243872 1612可以看到的是和上面計算的1266kb也是比較接近的。
??這個時候發(fā)現我的定時任務中有一個監(jiān)控腳本,里面是對es集群的監(jiān)控(每分鐘運行一次),curl請求獲取es信息使用的是http方式,之前做過一些測試http是不會導致dentry上升的。里面用到https的地方就是如果集群由問題會使用釘釘進行報警,請求釘釘的時候使用的是https的方式。所以一開始沒有懷疑這個腳本,這個時候嘗試把對應的定時任務關了,結果上面那個腳本顯示的結果真的不增加了,看來真的是這個腳本的鍋…
這個時候感覺好奇怪,釘釘上也滅有啥信息啊,不可能每分鐘都發(fā)信息啊。手動運行了一下腳本,發(fā)現發(fā)送釘釘之后返回的錯誤碼顯示是消息格式有問題,所以不會顯示,又因為沒有發(fā)送成功就會每隔一分鐘發(fā)送一次。。。,因為是一個過時的監(jiān)控,所以一直沒有注意過,結果埋了一個坑。。
??到此,謎底才真正解開,舒服多了,總結一下就是自己寫的一個監(jiān)控腳本有bug,正好觸發(fā)了curl請求的一個底層bug,導致內核取dentry的分配消耗很大,
2.4.2 curl請求bug的發(fā)現
上面的排查過程說明了curl請求的鍋,這里聊一下自己驗證curl請求的鍋的時候使用的腳本。
** 1. 大量發(fā)送curl請求的腳本 **
有些時候請求量小的話不太容易看出來
??測試中發(fā)現這個不一定每次都會大量增加,可能因為某些時間點的回收導致的,但是如果是量足夠大的話,比如上萬次,肯定會增加的比較大。
同時,我們還可以輔助用一個腳本來判斷。
** 2. 跟蹤dentry分配的細節(jié) **
為了確認有dentry分配,當然,我們可以使用上面的那個跟蹤d_alloc的腳本實現,還有一種是可以跟蹤dentry具體對應的目錄,當然,還是使用強大的systemTap
使用 stap detail_dentry.stp > d05.log &來進行記錄
然后查看對應文件的記錄數據。
根據對應的 ** /etc/pki/nssdb/.3851895989_dOeSnotExist_.db** 關鍵字也能夠google出對應的bug來。
這個bug對應的記錄在這里
從這里綜合的信息來看,這個bug描述來看,好像是nss的一個bug,出現在3.14.3-4.el6_4.x86_64,如果想要徹底修復,需要經NSS 升級到3.16.1-4
但是實際上在低一些的版本也提供了修復方案,只有是版本大于等于nss-softokn-3.14.3-12.el6 即可通過設置一個變量 export NSS_SDB_USE_CACHE=yes來解決
查看本機的版本
可以看到本機是nss-softokn 版本是3.14.3-23.el6_7 屬于bug修復的版本,但不是終極修復版本。
通過在監(jiān)控腳本的開頭添加
最終解決問題。
3 . 數據樣例附
cat log_slab.log time slab-kb dentry-kb diff 2019.10.09-21:01:19 2929348 2685424 243924 2019.10.09-21:01:28 2929204 2685424 243780 2019.10.09-21:01:30 2928804 2685424 243380 2019.10.09-21:01:31 2928736 2685424 243312 2019.10.09-21:06:01 2935132 2691656 243476 #log_file="/home/deploy/search/test_data/systemtap_dependency/dentry_record_/log_slab.log" log_file="$(cd $(dirname $0);pwd)/log_slab.log" echo "$log_file" if [ ! -e $log_file ] ;thenecho "not exist"echo "2019.10.09-21:01:19 2929348 2685424 243924" > $log_filefi dentry_size=$( /usr/bin/slabtop -o|grep dentry |awk '{print $7}'|sed -e "s/K//g") slab_size=$( cat /proc/meminfo | grep Slab|awk '{print $2}') cur_time=$(date +'%Y.%m.%d-%H:%M:%S') diff_s_d=$(echo "${slab_size}-${dentry_size}"|bc)lastest_dentry=$(tail -n1 ${log_file}|awk '{print $3}') diff_dentry=$(echo "${dentry_size} - ${lastest_dentry}"|bc)res="${cur_time} ${slab_size} ${dentry_size} ${diff_s_d} ${diff_dentry}" #echo "$res" >> /home/deploy/search/test_data/systemtap_dependency/dentry_record_/log_slab.log echo "$res" >> ${log_file}參考
感謝大家分享,讓我得以查到這個問題
https://blog.csdn.net/mans1516/article/details/51434408
https://zhuanlan.zhihu.com/p/43133085
https://blog.arstercz.com/centos-%E7%B3%BB%E7%BB%9F-slab-dentry-%E8%BF%87%E9%AB%98%E5%BC%95%E8%B5%B7%E7%B3%BB%E7%BB%9F%E5%8D%A1%E9%A1%BF%E5%88%86%E6%9E%90%E5%A4%84%E7%90%86/
總結
以上是生活随笔為你收集整理的free和top显示可用内存不一致的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 内存模型杂论
- 下一篇: elasticsearch使用优化备忘