背景介紹
我負(fù)責(zé)一個(gè)物聯(lián)網(wǎng)平臺(tái)的開(kāi)發(fā)與運(yùn)維工作,2020年12月18日的一天,恰逢周五,本來(lái)以為可以劃劃水就可以過(guò)愉快的周末了,一大早讓我看下昨天的設(shè)備上報(bào)的數(shù)據(jù)情況,打開(kāi)瀏覽器輸入網(wǎng)址,無(wú)法正常登錄,提示服務(wù)器內(nèi)部錯(cuò)誤!我心里一想不對(duì)呀,本人照看的系統(tǒng)已經(jīng)穩(wěn)定運(yùn)行很長(zhǎng)一段時(shí)間了,怎么會(huì)這樣?都坐下,不要慌,我們慢慢的來(lái)處理。
處理過(guò)程
首先,通過(guò)我的FinalShell工具通過(guò)ssh登錄服務(wù)器,一眼就看到了讓我震驚的結(jié)果,我開(kāi)始慌了。。。
有個(gè)java進(jìn)程居然內(nèi)存和CPU占用都這么高,然后我簡(jiǎn)單看了一下進(jìn)程號(hào),定位到是物聯(lián)網(wǎng)平臺(tái)的后臺(tái)服務(wù)進(jìn)程(由于該平臺(tái)是采用docker部署的,因此需要進(jìn)入docker容器內(nèi)部進(jìn)行查看)

docker exec -it cfe1916c608a /bin/bash
通過(guò)docker exec進(jìn)入容器內(nèi)部,首先還是使用我們比較常用top看下情況

果然,與我們看到的現(xiàn)象一致,有個(gè)java進(jìn)程RES占有高達(dá)8.6g,%CPU高達(dá)277.0,這時(shí)突然有些興奮呀,我又可以躺坑了,事后會(huì)被作為經(jīng)驗(yàn)文章分享出來(lái),也就是你現(xiàn)在看到的樣子??。然后我們一步步來(lái)分析和處理。。。
接下來(lái),我們主要從CPU升高的方向先入手分析。復(fù)制該進(jìn)程的id號(hào)8,通過(guò)執(zhí)行top -Hp查看當(dāng)前進(jìn)程的各個(gè)線程執(zhí)行情況
top -Hp 8

可以看到,前面幾個(gè)線程的CPU占用都非常高,我們隨機(jī)挑選一個(gè)PID為13的線程進(jìn)行分析,需要注意的是,在jstack命令展示中,線程id都是轉(zhuǎn)化成的十六進(jìn)制形式,使用以下命令打印線程id的16進(jìn)制結(jié)果

重點(diǎn)來(lái)了,執(zhí)行jstack查看堆棧信息情況
jstack 8 |grep d

終于發(fā)現(xiàn)因?yàn)槭裁磳?dǎo)致CPU升高了,可以看到a,b,c,d都是代表GC線程(PS:上圖中的10~13這幾個(gè)都是GC線程),我們大概猜測(cè)應(yīng)該就是因?yàn)镚C進(jìn)行頻繁的垃圾回收,導(dǎo)致其他業(yè)務(wù)無(wú)法正常的工作。好的,我們還是通過(guò)jstat驗(yàn)證一下
jstat -gcutil 8 2000 10

果然,這個(gè)FGC與FGCT顯示的fullGC的次數(shù)和時(shí)間是不是讓你頭皮發(fā)麻 ??。然后,給大家一個(gè)小技巧,我們先通過(guò)jmap -histo命令先大致查看下堆內(nèi)存的對(duì)象數(shù)量和大小情況
jmap -histo:live 8 | more

從分析中我們可以看到【B與【C占有都非常高,這是什么玩意?其實(shí)這個(gè)Byte和Char數(shù)組,大膽猜測(cè)可能是有大量的String字符串。。。
class name是對(duì)象類型,說(shuō)明如下:
- B byte
- C char
- D double
- F float
- I int
- J long
- Z boolean
- [ 數(shù)組,如[I表示int[]
- [L+類名 其他對(duì)象
我們還是來(lái)好好分析下,到底是什么原因?qū)е碌腉C經(jīng)過(guò)這么努力在釋放堆內(nèi)存還是釋放不出來(lái),幫幫JVM虛擬機(jī)診斷下病因,這個(gè)時(shí)候其實(shí)大家應(yīng)該已經(jīng)知道我們應(yīng)該看下堆內(nèi)存里面到底是什么
首先,我們通過(guò)jmap -dump導(dǎo)出現(xiàn)在JVM的堆內(nèi)存日志情況
jmap -dump:format=b,file=dump.dat 8

然后,從服務(wù)器下載dump.dat文件到本機(jī),通過(guò)Eclipse Memory Analyzer工具對(duì)其進(jìn)行分析查看
內(nèi)存使用整體情況

直接點(diǎn)擊下方Reports→Leak Suspects鏈接來(lái)生成報(bào)告,查看導(dǎo)致內(nèi)存泄露的罪魁禍?zhǔn)?/p>

從圖中可以看出一個(gè)可疑對(duì)象消耗了近93.43%的內(nèi)存。接著往下看,點(diǎn)擊Details查看詳細(xì)情況

備注:
Shallow Heap 為對(duì)象自身占用的內(nèi)存大小,不包括它引用的對(duì)象。
Retained Heap 為當(dāng)前對(duì)象大小 + 當(dāng)前對(duì)象可直接或間接引用到的對(duì)象的大小總和
點(diǎn)擊dominator_tree查看entries heap對(duì)象引用關(guān)系tree進(jìn)行分析

最終通過(guò)點(diǎn)擊entries引用tree層層查看對(duì)象的引用情況,查看熟悉的東西,定位到好像與device_log_mtu_2020-12這個(gè)地方有關(guān)系

其實(shí)我們已經(jīng)大概定位到問(wèn)題出現(xiàn)位置了,接下來(lái)其實(shí)就要結(jié)合業(yè)務(wù)系統(tǒng)情況來(lái)追查業(yè)務(wù)情況與分析系統(tǒng)代碼,最終定位到問(wèn)題的原因:
以下截圖是我與系統(tǒng)的技術(shù)開(kāi)發(fā)人員的交流,最終大概定位到問(wèn)題的原因:因?yàn)榇a的邏輯bug導(dǎo)致出現(xiàn)死循環(huán)

總結(jié)
先通過(guò)top命令查看CPU與內(nèi)存情況,查看是什么進(jìn)程的CPU和內(nèi)存飆高,如果是CPU比較高,則可以通過(guò)top -Hp <pid>命令查看當(dāng)前進(jìn)程的各個(gè)線程運(yùn)行情況,找出CPU過(guò)高的線程以后,將其線程id轉(zhuǎn)換為十六進(jìn)制的形式后,然后通過(guò)jstack日志查看改線程的工作狀態(tài)和調(diào)用棧情況,這里有兩種情況:
- 如果是正常的用戶線程,則通過(guò)該線程的堆棧信息查看其具體在哪出代碼運(yùn)行比較消耗CPU;
- 如果該線程是
VM Thread,則通過(guò)jstat -gcutil <pid> <period> <times>命令監(jiān)控當(dāng)前系統(tǒng)的GC狀況,然后通過(guò)jmap dump:format=b,file=<filepath> <pid>導(dǎo)出系統(tǒng)當(dāng)前的內(nèi)存數(shù)據(jù)。導(dǎo)出之后將內(nèi)存情況放到eclipse的mat工具中進(jìn)行分析即可得出內(nèi)存中主要是什么對(duì)象比較消耗內(nèi)存,進(jìn)而可以處理相關(guān)代碼
如果企業(yè)內(nèi)部本身沒(méi)有相關(guān)監(jiān)控與運(yùn)維工具,那我們可以使用JDK本身提供了一些監(jiān)控分析工具(jps、jstack、jmap、jhat、jstat、hprof等),通過(guò)靈活運(yùn)用這些工具幾乎能夠定位Java項(xiàng)目80%的性能問(wèn)題。最后在這里推薦一個(gè)Alibaba開(kāi)源的Java診斷工具Arthas可以幫助你解決:
- 這個(gè)類從哪個(gè) jar 包加載的?為什么會(huì)報(bào)各種類相關(guān)的 Exception?
- 我改的代碼為什么沒(méi)有執(zhí)行到?難道是我沒(méi) commit?分支搞錯(cuò)了?
- 遇到問(wèn)題無(wú)法在線上 debug,難道只能通過(guò)加日志再重新發(fā)布嗎?
- 線上遇到某個(gè)用戶的數(shù)據(jù)處理有問(wèn)題,但線上同樣無(wú)法 debug,線下無(wú)法重現(xiàn)!
- 是否有一個(gè)全局視角來(lái)查看系統(tǒng)的運(yùn)行狀況?
- 有什么辦法可以監(jiān)控到JVM的實(shí)時(shí)運(yùn)行狀態(tài)?
- 怎么快速定位應(yīng)用的熱點(diǎn),生成火焰圖?
參考資料
系統(tǒng)運(yùn)行緩慢,CPU 100%,以及Full GC次數(shù)過(guò)多問(wèn)題的排查思路
JVM性能調(diào)優(yōu)監(jiān)控工具jps、jstack、jmap、jhat、jstat、hprof_憧憬的專欄-CSDN博客
本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布!