前言
之前線上有過一兩次OOM的問題,但是每次定位問題都有點手足無措的感覺,剛好利用星期天,以測試環境為模版來學習一下Linux常用的幾個排查問題的命令。
也可以幫助自己在以后的工作中快速的排查線上問題。
jmap命令
jmap -heap pid 輸出當前進程 JVM 堆新生代、老年代、持久代等請情況,GC 使用的算法等信息
jmap -histo:live {pid} | head -n 10 輸出當前進程內存中所有對象包含的大小
jmap -dump:format=b,file=/usr/local/logs/gc/dump.hprof {pid} 以二進制輸出檔當前內存的堆情況,然后可以導入 MAT 等工具進行
1、 jmap -heap pid
輸出當前進程JVM堆新生代、老年代、持久代等情況,GC使用的算法等信息。

2、jmap -histo:live {pid} | head -n 10 輸出當前進程內存中所有對象包含的大小
輸出當前進程內存中所有對象實例數 (instances) 和大小 (bytes), 如果某個業務對象實例數和大小存在異常情況,可能存在內存泄露或者業務設計方面存在不合理之處。

jmap -dump:
命令如下:
mkdir logs
jmap -dump:format=b,file=/tmp/logs/dump.hprof {pid}
-dump:formate=b,file= 以二進制輸出當前內存的堆情況至相應的文件,然后可以結合 MAT 等內存分析工具深入分析當前內存情況。
也可以通過JVM參數配置OOM時自動dump當前內存鏡像文件。 -XX:+HeapDumpOnOutOfMemoryError 和-XX:HeapDumpPath所代表的含義就是當程序出現OutofMemory時,將會在相應的目錄下生成一份dump文件,而如果不指定選項-XX:HeapDumpPath則在當前目錄下生成dump文件。
確保應用發生 OOM 時 JVM 能夠保存並 dump 出當前的內存鏡像。
當然,如果你決定手動 dump 內存時,dump 操作占據一定 CPU 時間片、內存資源、磁盤資源等,因此會帶來一定的負面影響。
此外,dump 的文件可能比較大 , 一般我們可以考慮使用 zip 命令對文件進行壓縮處理,這樣在下載文件時能減少帶寬的開銷。
下載 dump 文件完成之后,由於 dump 文件較大可將 dump 文件備份至制定位置或者直接刪除,以釋放磁盤在這塊的空間占用。
dump 日志分析
MAT(Memory Analyzer Tool),一個基於 Eclipse 的內存分析工具,是一個快速、功能豐富的 JAVA heap 分析工具,它可以幫助我們查找內存泄漏和減少內存消耗。
使用內存分析工具從眾多的對象中進行分析,快速的計算出在內存中對象的占用大小,看看是誰阻止了垃圾收集器的回收工作,並可以通過報表直觀的查看到可能造成這種結果的對象。
具體可以參考:Java內存分析工具MAT(Memory Analyzer Tool)安裝使用實例 : https://blog.csdn.net/jin_kwok/article/details/80326088 和 基於Java內存dump文件分析解決內存泄漏問題 : https://www.jianshu.com/p/2cf7169ba1c4
jstack命令
printf '%x\n' tid --> 10 進制至 16 進制線程 ID(navtive 線程) %d 10 進制
jstack pid | grep tid -C 30 --color ps -mp 8278 -o THREAD,tid,time | head -n 40
某 Java 進程 CPU 占用率高,我們想要定位到其中 CPU 占用率最高的線程。
(1) 先利用top命令找到CPU占用高的進程pid
也可以通過ps -ef | grep 應用名 來快速定位自己應用的pid

顯示pid:29080
(2) 利用 top 命令可以查出占 CPU 最高的線程 pid (先找到該pid 29080下所有的線程數據)
可以看到占用cpu資源最高的為29173
(3) 占用率最高的線程 ID 為29173,將其轉換為 16 進制形式 (因為 java native 線程以 16 進制形式輸出)
printf '%x\n' 29173
(4) 利用 jstack 打印出 java 線程調用棧信息
jstack 29080 | grep '0x71f5' -A 50 --color

可以看到這個線程是在做kafka相關的操作。因為這是測試,所以並不是因為CPU真的占用過高的情況。
更多內容也可以參考:
如何使用jstack分析線程狀態 : https://www.jianshu.com/p/6690f7e92f27
通過jstack與jmap分析一次線上故障: https://www.cnblogs.com/kingszelda/p/9034191.html
jinfo命令
jinfo可以用來查看正在運行的java運用程序的擴展參數。
查看pid對應的JVM參數,可以到 PerfMa : http://xxfox.perfma.com/jvm/check 校驗參數的正確性
jinfo -flags pid

拿到Command line后面的配置參數到perfma中驗證查詢:

這里面好多功能可以去使用。
jstat命令
jstat:Java Virtual Machine statistics monitoring tool JDK自帶的一個輕量級小工具。
jstat顯示GC執行的情況
jstat -gc 12538 5000
即會每5秒一次顯示進程號為12538的java進成的GC情況

說明:
S0C、S1C、S0U、S1U:Survivor 0/1區容量(Capacity)和使用量(Used)
EC、EU:Eden區容量和使用量
OC、OU:年老代容量和使用量
PC、PU:永久代容量和使用量
YGC、YGT:年輕代GC次數和GC耗時
FGC、FGCT:Full GC次數和Full GC耗時
GCT:GC總耗時
顯示內容說明如下(部分結果是通過其他其他參數顯示的,暫不說明):
S0C:年輕代中第一個survivor(幸存區)的容量 (字節)
S1C:年輕代中第二個survivor(幸存區)的容量 (字節)
S0U:年輕代中第一個survivor(幸存區)目前已使用空間 (字節)
S1U:年輕代中第二個survivor(幸存區)目前已使用空間 (字節)
EC:年輕代中Eden(伊甸園)的容量 (字節)
EU:年輕代中Eden(伊甸園)目前已使用空間 (字節)
OC:Old代的容量 (字節)
OU:Old代目前已使用空間 (字節)
PC:Perm(持久代)的容量 (字節)
PU:Perm(持久代)目前已使用空間 (字節)
YGC:從應用程序啟動到采樣時年輕代中gc次數
YGCT:從應用程序啟動到采樣時年輕代中gc所用時間(s)
FGC:從應用程序啟動到采樣時old代(全gc)gc次數
FGCT:從應用程序啟動到采樣時old代(全gc)gc所用時間(s)
GCT:從應用程序啟動到采樣時gc用的總時間(s)
總結
一般分析CPU或者內存異常情況可以通過以下幾步:
- 查看日志
- 查看CPU情況
- 查看TCP情況
- 查看java線程,jstack
- 查看java堆,jmap
- 通過MAT分析堆文件,尋找無法被回收的對象
參考:
Java線上問題排查思路與工具使用 : https://blog.csdn.net/GitChat/article/details/79019454

