有時(shí)候好好的程序放到生產(chǎn)服務(wù)器上一段時(shí)間后,就會(huì)發(fā)現(xiàn)服務(wù)器響應(yīng)緩慢,進(jìn)而進(jìn)一步發(fā)現(xiàn)是cpu過(guò)高,于是就慌了,造成cpu過(guò)高的原因很多,不過(guò)大多是由于資源吃緊造成,例如:sql執(zhí)行過(guò)慢,程序里存在死循環(huán),數(shù)據(jù)庫(kù)連接未釋放,網(wǎng)絡(luò)阻塞導(dǎo)致的第三方框架代碼出現(xiàn)死循環(huán),大量的操作導(dǎo)致死鎖等,遇到此類(lèi)問(wèn)題不必緊張,coder君手把手教你克服心理障礙。
分析點(diǎn)擊抽樣器->CPU->查看CPU樣例,發(fā)現(xiàn)endlessLoop()方法最耗CPU(這里有2個(gè)方法 loop和endlessLoop)
查看線程cpu耗時(shí),發(fā)現(xiàn)main線程最耗時(shí),點(diǎn)擊增量,可以從此刻觀察,cpu耗時(shí)的增長(zhǎng)速率
查看線程dump,主要觀察main線程,發(fā)現(xiàn)main線程當(dāng)前狀態(tài)下一直在執(zhí)行 CpuTest.endlessLoop(CpuTest.java:14)
,這里可以定位問(wèn)題位置,同時(shí)細(xì)心的童鞋可以觀察看后面執(zhí)行System.out.println(“”);方法是要先加鎖的。
截圖一段,我生產(chǎn)服務(wù)器(tomcat+springmvc)main線程的情況,其實(shí)只想說(shuō)明web項(xiàng)目啟動(dòng)的main方法在中間件里。
VisualVm只能定位JVM的cpu情況,但是生產(chǎn)主機(jī)上不光是Java程序,這時(shí)我們要采取另外的方案。
1.看監(jiān)控數(shù)據(jù)是否正常,cpu,mem。
CPU占用1.5左右(100-98.0id) 內(nèi)存占用50%(435/100*100%=43.5%) 阿里云監(jiān)控內(nèi)存大小轉(zhuǎn)成實(shí)際占用內(nèi)存大小,類(lèi)似windows ,平均負(fù)載 0.1 差不多,其他幾個(gè)參數(shù),這里暫不介紹。
2.假設(shè)異常,找到異常的PID。
這里推薦 htop
(清晰進(jìn)程,線程, 命令行 ,排序支持鼠標(biāo)雙擊,過(guò)濾,kill程序,標(biāo)記某個(gè)線程或者進(jìn)程,安裝apt-get install htop
)
如果你沒(méi)有服務(wù)器上安裝軟件的權(quán)限的話,就老老實(shí)實(shí)用 top
。通過(guò) top
命令(默認(rèn)3秒刷新,回車(chē)空格手動(dòng)刷新, top -d 5
5秒刷新,也可以進(jìn)入top后輸入 d
設(shè)置刷新時(shí)間, top -p 4360
監(jiān)控指定進(jìn)程),然后按X ,默認(rèn)按照CPU%排序,查看系統(tǒng)運(yùn)行情況,如果想強(qiáng)制按CPU 降序,則輸入大寫(xiě)P,如果強(qiáng)制按內(nèi)存降序,則輸入大寫(xiě)M(top命令是交互式的)。
解讀:
1).現(xiàn)在系統(tǒng)時(shí)間 10:18.44 ,系統(tǒng)一直運(yùn)行了 131天16小時(shí)51分,當(dāng)前有1個(gè)用戶登錄系統(tǒng)(相同賬號(hào)也算不同用戶),平均負(fù)載分別為0.00,0.01,0.05(分別為1分鐘,5分鐘,15分鐘的負(fù)載情況,load average是每隔5秒鐘檢查一次活躍的進(jìn)程數(shù),用特定的算法得到的數(shù)值,然后除以邏輯CPU數(shù)量,如果負(fù)載持續(xù)大于cpu個(gè)數(shù),則表明負(fù)載過(guò)高) 。大概可以看出系統(tǒng)負(fù)載很低,運(yùn)行狀態(tài)健康。
2).當(dāng)前一共有103個(gè) 進(jìn)程 ,處于運(yùn)行的有2個(gè),處于休眠狀態(tài)的有101個(gè),處于停止?fàn)顟B(tài)的有0個(gè),處于僵尸狀態(tài)的有0個(gè)。大概可以看出系統(tǒng)進(jìn)程總數(shù)較少,環(huán)境比較單純,運(yùn)行中的進(jìn)程不多。
3). 0.3 us
用戶空間占用CPU 0.3%, 0.7 sy
內(nèi)核占用CPU 0.7%, 0.0 ni
改變過(guò)優(yōu)先級(jí)的進(jìn)程占用CPU的百分比, 98.0 id
空閑CPU的百分比為98.0 , 0.3 wa
IO等待所占用CPU的百分比為0.3, 0.3 hi
硬中斷(Hardware IRQ)占用CPU的百分比為0.3(外設(shè)給CPU的異步信號(hào)(中斷),例如:網(wǎng)卡收到數(shù)據(jù)包), 0.0 si
軟中斷占用cpu的百分比為0(軟件本身給操作系統(tǒng)內(nèi)核的中斷信號(hào),通常由硬中斷處理程序?qū)Σ僮飨到y(tǒng)內(nèi)核的中斷), 0.3 st
虛擬機(jī)被hypervisor偷去CPU的時(shí)間。
4). KiB Mem
代表內(nèi)存占用, 1016272 total
內(nèi)存總的大小1g(以kb為單位), 941492 used
使用中的內(nèi)存總量為0.9g, 74780 free
空閑內(nèi)存總量為74m(嚇一跳吧,才74M,這個(gè)不是實(shí)際剩余的內(nèi)內(nèi)存大小) , 115900 buffers
緩存的內(nèi)存量為115m, 389836 cached
cached大小380M??臻e內(nèi)存總量只有74m,如果是windows去理解的話,此臺(tái)服務(wù)器已經(jīng)快掛了,實(shí)際內(nèi)存大小等于74M+ buffers+cached = 580m(哈哈,夠用,才占用一半呢), linux的內(nèi)存管理和windows是不一樣的,Linux會(huì)借用空閑的內(nèi)存當(dāng)作磁盤(pán)緩存, 磁盤(pán)數(shù)據(jù)緩存會(huì)讓linux運(yùn)行的更快,它永遠(yuǎn)不會(huì)從程序中拿出內(nèi)存,它沒(méi)有任何缺點(diǎn),只是會(huì)混淆新手,如果你的應(yīng)用程序需要更多的內(nèi)存,他們會(huì)回收一部分用作磁盤(pán)數(shù)據(jù)緩存的物理內(nèi)存,返回給應(yīng)用程序,這個(gè)過(guò)程不需要啟動(dòng)交換,磁盤(pán)緩存(Disk caching)是不能禁用的, 但是可以釋放磁盤(pán)緩存 1).只釋放pagecache(文件緩存) echo 1 > /proc/sys/vm/drop_caches
2).釋放dentries和inodes echo 2 > /proc/sys/vm/drop_caches
3).釋放pagecache,dentries和inodes echo 3 > /proc/sys/vm/drop_caches
具體原理可以查看 linuxatemyram
我們利用 free
進(jìn)一步證實(shí)上面的內(nèi)容
2).435288代表 -buffers/cache ( 應(yīng)用程序?qū)嶋H使用330m內(nèi)存 ) 580984 代表 +buffers/cache( 實(shí)際剩余內(nèi)存大小580m )。
3).重要等式 total = used + free
used(-buffers/cache) = used(Mem)-buffers(Mem)-cached(Mem)
free(+buffers/cache) = free(Mem) + buffers(Mem) +cached(Mem)
。
4). free
命令的值是從 /proc/meminfo 文件里讀到的。
5).Swap 交換區(qū) 總共大小 0 kb,已經(jīng)使用0kb,釋放了0Kb, 如果swap used > 0,則可以說(shuō)明系統(tǒng)內(nèi)存瓶頸了 。
6).內(nèi)存總和 free -t
總和等于total(Mem) + total(Swap)。
7).一般常用命令 free -s 3
每3秒觀察一次內(nèi)存使用情況。
占用cpu最高的進(jìn)程是java,PID為28628,USER進(jìn)程所有者root,PR優(yōu)先級(jí)20,NI為0(負(fù)值優(yōu)先級(jí)高,正值優(yōu)先級(jí)低,PR=NI+20) ,VIRT進(jìn)程使用虛擬內(nèi)存1G(java進(jìn)程最高只能占用到1G),RES進(jìn)程實(shí)際使用的物理內(nèi)存270M(不包含swap和shared),SHR共享內(nèi)存大小5M,S進(jìn)程狀態(tài)隨眠狀態(tài)(S睡眠,R運(yùn)行,T停止或被跟蹤,Z僵尸,D不可中斷睡眠態(tài)),%CPU占用CPU 0.7%,%MEM占用內(nèi)存27%,TIME+進(jìn)程使用cpu的時(shí)間54分40.12秒??傮w看java進(jìn)程運(yùn)行良好。
3.這里假設(shè)PID為16368的 進(jìn)程 占用CPU比較高(因?yàn)樽隽硕啻螌?shí)驗(yàn),所以PID沒(méi)辦法和上面的28628保持一致),
先用 ps -ef | grep java
,也可以用 htop
filter java,也可以 jps -v
找到 java的進(jìn)程ID 16368(只能查看當(dāng)前用戶的java pid,不太建議使用,-v顯示詳細(xì)信息,也可以不加)
?代表終端設(shè)備未知,Sl代表休眠狀態(tài)多線程
Linux通過(guò)進(jìn)程查看線程的方法 1). htop
按t(顯示進(jìn)程線程嵌套關(guān)系)和H(顯示線程) ,然后F4過(guò)濾進(jìn)程名。2). ps -eLf | grep java
(快照,帶線程命令,e是顯示全部進(jìn)程,L是顯示線程,f全格式輸出) 3). pstree -p <pid>
(顯示進(jìn)程樹(shù),不加pid顯示所有) 4). top -Hp <pid>
(實(shí)時(shí)) 5). ps -T -p <pid>
(快照) 推薦程度按數(shù)字從小到大 。
4.利用 jstack 16368 > 2016-1-21.tdump
(jstack是jdk自帶的生成java stack和native stack的工具) 把threaddump輸出到文件里,這里假設(shè)PID為16369(一般比進(jìn)程號(hào)+1的是main線程,如果存在main線程貌似最先分配)的線程占用CPU最高,將其ID轉(zhuǎn)成16進(jìn)制0x3ff1(使用命令 printf '%x/n' 16369
輸出3ff1)。
5.搜索threaddump文件,nid= 0x3ff1的線程堆棧,通過(guò)堆棧信息,就可以定位到占用CPU最高的代碼地方(這里是正常的)。
好啦,CPU沖高問(wèn)題排查完畢,有了這樣的知識(shí)體系,以后完全可以舉一反三,coder君同時(shí)附上其他常用配合命令。
每隔一秒顯示新生代,老年代,持久代垃圾回收情況
jstat -gcutil 16368 1000ms
參考 jstat -help
,也可以查看遠(yuǎn)程主機(jī)的GC情況,需要遠(yuǎn)程主機(jī)開(kāi)啟jstatd服務(wù)。
觀察堆內(nèi)存情況
jmap -dump:format=b,file=2016-1-21.mdump 16368
生成堆dump,放到mat或者vvm進(jìn)行分析,上篇分析OOM有講到。
查看jvm系統(tǒng)參數(shù)
jinfo 16368
【深入淺出-VisualVM】(1):遠(yuǎn)程調(diào)試【深入淺出-VisualVM】(2):分析堆內(nèi)存OOM【深入淺出-VisualVM】(3):分析PermGenOOM
原文 http://mousycoder.com/2016/02/14/thinking-in-visualvm/thinking-in-visualvm-4/
聯(lián)系客服