一、首先說可用工具
1、jmap可以檢視目前Java程序的記憶體占用,把記憶體快照dump出來
用法:jmap [option] <pid>
常用指令
jmap -heap pid
主要顯示堆的記憶體使用情況,包括分代情況,每個代的總容量、已使用記憶體、可使用記憶體,如圖:
jmap樣例
jmap -dump:live,format=b,file=xxx.xxx [pid]
目前Java程序的記憶體占用情況導出來,用記憶體分析工具分析,如MAT
2、jstack可以檢視線程運作情況,是否有死鎖,cpu過高問題
用法:jstack [option] <pid>
常用指令
jstack -l pid 生成虛拟機目前時刻的線程快照
需要關注的狀态:deadlock:死鎖,blocked:線程被阻塞,
通過top指令定位到cpu占用率較高的線程之後,接着使用jstack pid指令來檢視目前java程序的堆棧狀态,将線程id轉換成16進制來檢視,建議隔段時間再執行一次stack指令,再一次擷取thread dump,畢竟兩次拍片結果(jstack)對比
3、jstat指令來檢視垃圾回收的情況,特别是fullgc
常用指令:
jstat -gc <pid>
jstat樣例
參數介紹:
S0C:年輕代中第一個survivor(幸存區)的容量 (位元組)
S1C:年輕代中第二個survivor(幸存區)的容量 (位元組)
S0U:年輕代中第一個survivor(幸存區)目前已使用空間 (位元組)
S1U:年輕代中第二個survivor(幸存區)目前已使用空間 (位元組)
EC:年輕代中Eden(伊甸園)的容量 (位元組)
EU:年輕代中Eden(伊甸園)目前已使用空間 (位元組)
OC:Old代的容量 (位元組)
OU:Old代目前已使用空間 (位元組)
MC:metaspace(元空間)的容量 (位元組)
MU:metaspace(元空間)目前已使用空間 (位元組)
CCSC:目前壓縮類空間的容量 (位元組)
CCSU:目前壓縮類空間目前已使用空間 (位元組)
YGC:從應用程式啟動到采樣時年輕代中gc次數
YGCT:從應用程式啟動到采樣時年輕代中gc所用時間(s)
FGC:從應用程式啟動到采樣時old代(全gc)gc次數
FGCT:從應用程式啟動到采樣時old代(全gc)gc所用時間(s)
GCT:從應用程式啟動到采樣時gc用的總時間(s)
jstat -gc <pid> <n> n 間隔n毫秒列印,可以動态觀察
二、再說分析思路
1、如果CPU過高
使用top -Hp <pid>,jstack <pid》,十六進制轉換等一頓操作,找到占cpu高的線程,根據堆棧資訊可以找到占用CPU最多的線程,定位到具體的方法,分析原因,比如自旋鎖、遞歸等;
另外更多情況是,占用CPU最多的線程是GC線程,這就說明記憶體使用不當,瘋狂fullgc,j将CPU占滿,這時候需要分析記憶體占用。
2、頻繁fullgc
如果頻繁發生fullgc但是又一直沒有出現記憶體溢出,那麼表示fullgc實際上是回收了很多對象了,是以這些對象最好能在younggc過程中就直接回收掉,避免這些對象進入到老年代,對于這種情況,就要考慮這些存活時間不長的對象是不是比較大,導緻年輕代放不下,直接進入到了老年代,嘗試加大年輕代的大小,如果改完之後,fullgc減少,則證明修改有效。
3、如果已經發生oom
一般生産系統中都會設定當系統發生了OOM時,生成當時的dump檔案(-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base),如果沒設定,需要設定重新開機,嘗試複現問題。我們可以利用jvisualvm、MAT等工具來分析dump檔案,根據dump檔案找到異常的執行個體對象,和異常的線程(占用CPU高),定位到具體的代碼,然後再進行詳細的分析和調試。
三、最後總結
總之,調優不是一蹴而就的,需要分析、推理、實踐、總結、再分析,最終定位到具體的問題。
還希望補充什麼内容,大家在評論區留言,一起探讨!
點選關注,期待後續精彩内容!