Java的JVM效能監控與故障處理工具詳細介紹以及使用案例

語言: CN / TW / HK

「這是我參與11月更文挑戰的第9天,活動詳情檢視:2021最後一次更文挑戰」。

給一個系統定位問題的時候,知識、經驗是關鍵基礎,資料是依據。工具是運用知識處理資料的手段。這裡說的資料包括:執行日誌、異常堆疊、GC日誌、執行緒快照檔案(threaddump/javacore檔案)、堆轉儲快照(heapdump/hprof檔案)等。使用JVM命令和一檢視這個JVM引數,幫助我們排查、解決問題。

1 JDK的命令列工具

JDK的命令列工具大多數是對JDK/lib/tools.jar類庫的一層薄包裝而已,它們的主要功能程式碼是在tools類庫中實現的。Linux下的這些工具有的甚至是用shell指令碼編寫的。

常見工具如下:

名稱 主要作用
jps JVM Process Status Tool,顯示指定系統內所有的HotSpot虛擬機器程序
jstat JVM Statistics Monitoring Tool,用於收集HotSpot虛擬機器各方面的執行資料
jinfo Configuration Info for Java,顯示虛擬機器配置資訊
jmap Memory Map for Java,生成虛擬機器的記憶體轉出快照(heapdump檔案)
jhat JVM Heap Dump Browser,分析heapdump檔案,可以在瀏覽器檢視結果
jstack stack trace for java,顯示虛擬機器的執行緒快照

1.1 jps——虛擬機器程序狀況工具

列出正在執行的虛擬機器程序,並顯示虛擬機器執行主類名稱以及這些程序的本地虛擬機器唯一ID。

jps命令格式:

jps [ options ] [ hostid ]

jps可以通過RMI協議開啟了RMI服務的遠端虛擬機器程序狀態,hostid為RMI登錄檔中註冊的主機名。

jps常用的option選項:

選項 作用
-q 只輸出LVMID
-m 輸出虛擬機器程序啟動時傳遞給主類main()函式的引數
-l 輸出主類全名,如果程序執行的事jar包,輸出jar路徑
-v 輸出虛擬機器程序啟動時JVM引數

1.1.1 案例

案例類如下

java public class Jstat { /** * vm引數為 -Xms30m -Xmx30m -Xmn10m * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { Thread.sleep(1000000); } }

執行之後,使用jps命令,將會展示虛擬機器程序id和名字:

在這裡插入圖片描述

1.2 jstat——虛擬機器統計工具監視工具

jstat是用於監視虛擬機器各種執行狀態資訊的命令列工具。它可以顯示本地或者遠端虛擬機器程序中的類裝載、記憶體、垃圾回收、JIT編譯等執行資料,在沒有GUI圖形介面,只是提供了純文字控制檯環境的伺服器上,它將是執行期定位虛擬機器效能問題的首選工具。

jstat的命令格式:

jstat [option vmid [interval [s|ms] [count]] ]

  1. option: 引數選項
  2. -t: 可以在列印的列加上Timestamp列,用於顯示系統執行的時間
  3. -h: 可以在週期性資料資料的時候,可以在指定輸出多少行以後輸出一次表頭
  4. vmid: Virtual Machine ID( 程序的 pid)
  5. interval: 執行每次的間隔時間,單位為毫秒
  6. count: 用於指定輸出多少次記錄,預設則會一直列印
  7. 對於命令格式中的VMID和LVMID,如過是本地虛擬機器程序,VMID和LVMID是一致的,如果是遠端虛擬機器,那VMID的格式應當是:[protocol:] [//] lvmid[@hostname[:port]/servername]
  8. 引數interval 和count分別表示查詢的間隔和次數,如果省略這兩個引數,說明只查詢一次。

Jstat常用option選項:

選項 作用
-class 類裝載數量、解除安裝數量、總空間以及類狀態所消耗時間
-GC 監視Java堆容量狀況,包括Eden、Survivor、老年代、永久代等
-GCcapacity 監視Java堆最大、最小空間
-GCutil 關注已使用空間佔總空間的百分比
-GCcause 類似GCutil,額外輸出上次GC的原因
-GCnew 新生代GC狀況
-GCnewcapacity 與-GCnew類似,輸出主要關注使用到的最大、最小空間
-GCold 老年代GC狀況
-GColdcapacity 與-GCold類似,輸出主要關注使用到的最大、最小空間
-GCpermcapacity 輸出永久代使用到的最大、最小空間
-compiler 輸出JIT編譯過的方法和耗時
-printcompilation 輸出已經被JIT編譯的方法
-GCmetacapacity 元資料空間統計

1.2.1 案例

加上-GC顯示將會GC堆資訊,使用上一個案例,設定VM引數為 -Xms30m -Xmx30m -Xmn10m ,即初始記憶體30m,最大記憶體30m,年輕代10m。

執行程式,使用 jstat -gc 6128 命令結果如下,可以看到GC堆資訊:

在這裡插入圖片描述

S0C:年輕代中第一個Survivor(倖存區)的容量 (位元組) S1C:年輕代中第二個Survivor(倖存區)的容量 (位元組) S0U :年輕代中第一個Survivor(倖存區)目前已使用空間 (位元組) S1U :年輕代中第二個Survivor(倖存區)目前已使用空間 (位元組) EC :年輕代中Eden(伊甸園)的容量 (位元組) EU :年輕代中Eden(伊甸園)目前已使用空間 (位元組) OC :Old代的容量 (位元組) OU :Old代目前已使用空間 (位元組) MC:metaspace(元空間)的容量 (位元組) MU:metaspace(元空間)目前已使用空間 (位元組) YGC :從應用程式啟動到取樣時年輕代中GC次數 YGCT :從應用程式啟動到取樣時年輕代中GC所用時間(s) FGC :從應用程式啟動到取樣時old代(全GC)GC次數 FGCT :從應用程式啟動到取樣時old代(全GC)GC所用時間(s) GCT:從應用程式啟動到取樣時GC用的總時間(s)

從圖中可以看出,各項結果符合我們的VM引數設定的資訊。

1.3 jinfo——Java配置資訊工具

實時檢視和調整虛擬機器各項引數。

jinfo常用option選項:

選項 作用 案例
-flag 調整虛擬機器引數 jinfo -flag +PrintGCDetails 1479
-flags 檢視指定程序所有引數 jinfo -flags 1479
-sysprops 列印虛擬機器程序系統內容 jinfo -sysprops 1479
無參 列印所有 jinfo 1479

1.3.1 案例

使用上面的例子,加上vm引數資訊 -Xms30m -Xmx30m -Xmn10m。使用jinfo -flags 6128 ,從結果中可以找到我們設定的vm資訊。

在這裡插入圖片描述

1.4 jmap——Java記憶體對映工具

jmap命令用於生成堆轉儲快照。jmap的作用並不僅僅為了獲取dump檔案,它還可以查詢finalize執行佇列、java堆和永久代的詳細資訊。如空間使用率、當前用的是哪種收集器等。

jmap格式:

jmap [option] vmid

jmap常用option選項:

選項 作用
-dump 生成堆轉儲快照,格式為-dump:[live,]format=b,file=,不建議使用
-finalizerinfo 顯示在F-Queue中等待Finalizer執行緒執行finalize方法的物件
-heap 顯示java堆詳細資訊,回收器種類、引數配置、分代狀況等
-histo 顯示堆中物件統計資訊,包括類、例項數量、合計容量,會先觸發GC,再統計資訊,不建議使用
-permstat 檢視永久代記憶體狀態,比較耗時,會暫停應用,不建議使用
-F 強制生成dump快照,當-dump失效時,再使用此命令

1.4.1 案例

還是上面的例子。

使用jmap -heap 6128,可以看到我們的VM引數設定的資訊:

在這裡插入圖片描述

jmap -dump:live,format=b,file=‪C:\Users\lx\Desktop\test1.bin 9472

將生成堆轉儲快照,這裡我生成到桌面。後面可以使用jhat分析dump檔案。

在這裡插入圖片描述

1.5 jhat——虛擬機器堆快照分析工具

Sun JDK提供jhat與jmap搭配使用,來分析dump生成的堆快照。jhat內建了一個微型的HTTP/HTML伺服器,生成dump檔案的分析結果後,可以在瀏覽器中檢視。

1.5.1 案例

jhat test1.bin

test1.bin為上面生成的dump檔案。螢幕顯示“Server is ready.”的提示後,使用者在瀏覽器中鍵入http://localhost:7000就可以看到分析的結果了。

在這裡插入圖片描述

分析結果預設是以包為單位進行分組顯示,分析記憶體洩漏問題主要會使用到其中的“Heap Histogram”與OQL標籤的功能。前者可以找到記憶體中總容量最大的物件。後者是標準的物件查詢語言,使用類似SQL的語法對記憶體中的物件進行查詢統計。

在這裡插入圖片描述

1.6 jstack——Java堆疊跟蹤工具

jstack命令用於生成虛擬機器當前時刻的執行緒快照(一般稱為threaddump或者javacore檔案)。執行緒快照就是當前虛擬機器內每一條執行緒正在執行的方法堆疊集合,生成執行緒快照的主要目的是定位執行緒出現長時間停頓的原因,如執行緒死鎖、死迴圈、請求外部資源導致長時間等待等

jstack 格式:

jstack [option] vmid

jstack常見option選項:

選項 作用 案例
-m 如果呼叫本地方法,則顯示C/C++的堆疊 jstack -m 1479
-l 除堆疊外,顯示關於鎖的附加資訊 jstack -l 1479
-F 當正常輸出的請求不被響應時,強制輸出執行緒堆疊 jstack -F 1479

1.6.1 案例

jstack -l 9472

會輸出很多資訊,我們可以找到如下資訊:

在這裡插入圖片描述

可以看到,main執行緒正在限時等待——因為sleep的原因。

jstack 可以幫助我們用來分析執行緒資訊,比如死鎖,狀態等。

2 JDK的視覺化工具

JDK中除了提供大量的命令列工具外,還有兩個功能強大的視覺化工具:JConsole和VisualVM,這兩個工具是JDK的正式成員,沒有被貼上“unsupported and experimental”的標籤。

2.1 JConsole——Java監視與管理控制檯

從Java 5開始 引入了 JConsole,JConsole 是一個內建 Java 效能分析器。您可以輕鬆地使用 JConsole(或者,它更高階的 “近親” jvisualvm )來監控 Java 應用程式效能和跟蹤 Java 中的程式碼。(推薦使用升級版 JConsole 即 jvisualvm )

2.2 jvisualvm——多合一故障處理工具

JVisualVM是一個整合多個JDK命令列工具的視覺化工具。JVisualVM基於NetBeans平臺開發,它具備了外掛擴充套件功能的特性,通過外掛的擴充套件,可用於顯示虛擬機器程序及程序的配置和環境資訊(jps,jinfo),監視應用程式的CPU、GC、堆、方法區及執行緒的資訊(jstat、jstack)等。JVisualVM在JDK/bin目錄下。

以上兩個工具的使用可以看這篇文章:Java中的死鎖詳解以及解決辦法,其中的死鎖檢測部分有指導使用這兩個工具。

3 其他外部工具

除了JDK自帶的工具之外,使用外部工具,可以為我們提供更加強大功能。

3.1 MAT——記憶體分析工具

MAT(Memory Analyzer Tool),一個基於Eclipse的記憶體分析工具,是一個快速、功能豐富的JAVA heap分析工具,它可以幫助我們查詢記憶體洩漏和減少記憶體消耗。使用記憶體分析工具從眾多的物件中進行分析,快速的計算出在記憶體中物件的佔用大小,看看是誰阻止了垃圾收集器的回收工作,並可以通過報表直觀的檢視到可能造成這種結果的物件。

官網地址:https://www.eclipse.org/mat/

在這裡插入圖片描述

3.3.1 下載安裝

下載地址:https://www.eclipse.org/mat/downloads.php。這裡已經為大家準備好免費的:MemoryAnalyzer

在這裡插入圖片描述

將下載得到的MemoryAnalyzer-1.8.0.20180604-win32.win32.x86_64.zip進行解壓:

在這裡插入圖片描述

雙擊啟動!

3.3.2 使用

dump檔案就堆快照檔案,可以使用jmap、Jconsole、和Visual VM等工具匯出dump檔案,MAT也可以直接匯出dump檔案,當然也可以直接開啟現有的dump檔案。

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

檢視物件以及它的依賴:

在這裡插入圖片描述

檢視可能存在記憶體問題的分析:

在這裡插入圖片描述

3.3.3 案例

3.3.3.1 記憶體溢位程式碼

編寫程式碼,向List集合中新增100萬個字串,每個字串由1000個UUID組成。如果程式能夠正常執行,最後列印ok。

vm引數設定為 -Xms4m -Xmx4m -XX:+HeapDumpOnOutOfMemoryError HeapDumpOnOutOfMemoryError引數表示當JVM發生OOM時,自動生成DUMP檔案。

```java public class TestJvmOutOfMemory { public static void main(String[] args) { List list = new ArrayList<>(); for (int i = 0; i < 10000000; i++) { String str = ""; for (int j = 0; j < 1000; j++) { str += UUID.randomUUID().toString(); } list.add(str); } System.out.println("ok"); } }

```

3.3.3.2 執行測試

結果如下:

在這裡插入圖片描述

可以看到,發生了記憶體溢位,當發生記憶體溢位時,會dump檔案java_pid7264.hprof在工程目錄下:

在這裡插入圖片描述

3.3.3.3 匯入到MAT工具中進行分析

在這裡插入圖片描述

可以看到,有70.27%的記憶體由Object[]陣列佔有,所以比較可疑,這是非常有可能出現記憶體溢位的。

點選Details檢視詳情:

在這裡插入圖片描述

可以看到集合中儲存了大量的uuid字串。實際上這裡已經告訴我們是不是因此導致的記憶體溢位了,我們在details詳情的最下面可以看到:

在這裡插入圖片描述

就是這個大物件陣列(list集合使用陣列存放的元素),因為需要分配連續的記憶體,而記憶體不足導致的OOM。

3.2 GCEasy——GC日誌視覺化分析工具

直接將GC日誌log檔案,在官網http://gceasy.io/上開啟,,就能快速進行GC分析。好處是比較方便,壞處嘛,就是有可能因為網路原因打不開。

在這裡插入圖片描述

上傳後,點選“Analyze”按鈕,即可檢視報告。

3.2.1 案例

我們是用上面MAT的程式碼,設定vm引數為:

-Xms4m -Xmx4m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:./gc.log

表示將會輸出GC日誌,將gc.log上傳,開啟,有如下結果:

JVM memory size:分別使用了表格和圖形介面來展示了JVM堆記憶體大小。

在這裡插入圖片描述

Key Performance Indicators:這一部分是關鍵的效能指標

  1. Throughput表示的是吞吐量
  2. Latency表示響應時間
  3. Avg Pause GC Time 平均GC時間
  4. Max Pause GC TIme 最大GC時間

在這裡插入圖片描述

後面還有些引數,這裡不做作贅述。

相關文章:

  1. 《Java虛擬機器規範》
  2. 《深入理解Java虛擬機器》

如果有什麼不懂或者需要交流,可以留言。另外希望點贊、收藏、關注,我將不間斷更新各種Java學習部落格!