網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

一、監控java程式執行狀態

舉例用以下程式碼為例介紹如何使用常見的JVM命令列工具監控程式執行狀態,程式執行環境為Ubuntu 14。04、jdk 1。8。0_181:

package com。cn。test;import java。util。ArrayList;import java。util。List;public class HeapOOM { private static class TestObject{ private long[] data = new long[1024]; } public static void main(String[] args) { List objects = new ArrayList<>(); while (true){ objects。add(new TestObject()); try { Thread。sleep(50); } catch (InterruptedException e) { e。printStackTrace(); } } }}

上面的程式碼邏輯很簡單:死迴圈向List中add物件,最終會造成OOM,下面設定堆記憶體為200M執行程式:

java -verbose:gc -Xms200M -Xmx200M com。cn。test。HeapOOM

1。1 獲取java程序pid

要用命令列工具監控java程式執行情況的第一步是需要獲取java程式的pid,因為使用其他命令列工具需要java程序的pid作為引數,有兩種方式獲取pid。

1。1。1 linux的ps命令

執行程式的pid為22719

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

1。1。2 jdk自帶的命令列工具jps

只列出所有java程序的pid和主類名稱,-l引數列出全類名,-m引數列出給main函式的入參,-v引數列出傳遞給JVM的啟動引數,如圖可以看到jps獲取的pid和linux的ps命令獲取結果相同

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

1。2 Java應用執行過程中檢視配置資訊

可能在程式執行中需要檢視虛擬機器引數、系統屬性,便於我們確定引數配置是否有問題,可能還需要調整一些虛擬機器引數,使用jinfo(Configuration Info for Java)命令可以達到這個目的,-flags引數列出虛擬機器引數,-sysprops引數列出系統屬性,-flag引數可以檢視單個屬性或者修改屬性值,下面新增讓程式執行中列印GC日誌的虛擬機器引數-XX:+PrintGCDetails。

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

1。3 監控執行中JVM各個分割槽記憶體使用情況及垃圾回收情況

jstat(JVM Statistics Monitoring Tool)作為虛擬機器統計資訊監視工具可以獲取JVM各個分割槽記憶體使用、垃圾回收、類載入、JIT編譯等詳細資訊,並且透過設定引數可以做到實時列印監控資料,下圖為監控pid為23222的java程序,每1000ms列印一次JVM各個分割槽記憶體使用情況和gc的統計資訊,連續列印10次。

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

1。4 監控堆中物件統計資訊

透過監控堆空間中類、例項數量以及合計容量的統計資訊可以統計出應用程式中哪些物件被建立的最多,哪些物件佔用的記憶體量最大,哪些物件數量在持續增長,進而可以分析出應用中存在的隱患,例如記憶體溢位和記憶體洩漏問題,jmap(Memory Map for Java)是jdk自帶的Java記憶體映像工具可以完成這些工作,使用-histo引數可以按照佔用記憶體的量倒序列出堆中物件的類名、例項數、佔用記憶體量,如下圖所示兩次統計結果發現test。HeapOOM$TestObject,也就是HeapOOM的內部類TestObject的數量在增加,這種情況持續下去可能造成OOM,實際分析程式可以證明這一點。

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

jmap另一種更普遍的使用場景是用於生成堆轉儲快照(稱為heapdump或dump檔案),當然也可以在啟動時透過設定

-XX:+HeapDumpOnOutOfMemoryError

引數讓虛擬機器在OOM異常出現之後自動生成dump檔案。用於生成堆轉儲快照的命令格式為

jmap -dump:[live,]format=b,file= pid

live表示只dump出存活的物件。生成的堆轉儲快照可以用jdk自帶的jhat(JVM Heap Analysis Tool)虛擬機器堆轉儲快照分析工具進行分析或者使用更強大更專業的工具如Eclipse Memory Analyzer Tool(MAT)等。

二、分析OutOfMemoryError原因

上面示例程式執行下去最終會造成OOM,為了讓虛擬機器在OOM異常出現之後自動生成dump檔案用於分析原因,在執行之前新增

-XX:+HeapDumpOnOutOfMemoryError

引數:

java -verbose:gc -Xms100M -Xmx100M -XX:+HeapDumpOnOutOfMemoryError com。cntest。HeapOOM

執行後經過幾次垃圾回收最終發生OOM,同時生成了dump檔案,將生成的dump檔案匯入MAT(https://www。eclipse。org/mat/documentation/) 中,使用Histogram進行Class維度展示每個Class類的例項存在的個數,可以發現com。cn。test。HeapOOM$

TestObject和long陣列的Shawllow Heap佔比最大,所以可以確定發生OOM的原因是建立了大量com。cn。test。HeapOOM$TestObject將記憶體撐爆。

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

三、死鎖分析

執行下面的死鎖程式:

package test;public class DeadLock { public static void main(String[] args) { /** * 建立並啟動兩個執行緒t1、t2。兩個執行緒都要共享o1、o2兩個物件 */ Object o1 = new Object(); Object o2 = new Object(); Thread t1 = new Thread(new T1(o1, o2) , “執行緒1”); Thread t2 = new Thread(new T2(o1, o2) , “執行緒2”); t1。start(); t2。start(); }} class T1 implements Runnable { Object o1; Object o2; public T1(Object o1, Object o2) { this。o1 = o1; this。o2 = o2; } public void run() { //鎖o1和o2 synchronized (o1) { try { Thread。sleep(1000); } catch (InterruptedException e) { e。printStackTrace(); } synchronized (o2) { System。out。println(“o2”); } } }} class T2 implements Runnable { Object o1; Object o2; public T2(Object o1, Object o2) { this。o1 = o1; this。o2 = o2; } public void run() { //鎖o2和o1 synchronized (o2) { try { Thread。sleep(1000); } catch (InterruptedException e) { e。printStackTrace(); } synchronized (o1) { System。out。println(“o1”); } } }}

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

jastck –l pid

檢視執行緒堆疊發現“執行緒1”和“執行緒2”都處於阻塞狀態,相互持有對方的鎖,這就是造成死鎖的原因。

四、線上分析診斷工具Arthas

Arthas是Alibaba開源的Java診斷工具,透過全域性視角實時檢視應用load、記憶體、gc、執行緒的狀態資訊,並能在不修改應用程式碼的情況下,對業務問題進行診斷,包括檢視方法呼叫的出入參、異常,監測方法執行耗時,類載入資訊等,大大提升線上問題排查效率。

專案github地址:https://github。com/alibaba/arthas

專案文件地址:https://alibaba。github。io/arthas/index。html

詳細安裝、使用等方面的問題在文件中有完整的描述,這裡只通過幾個使用案例大致瞭解一下這款工具的功能,執行下面的例項程式:

package test; import java。util。UUID; public class TestCase { public static String uuid(){ return UUID。randomUUID()。toString()。replaceAll(“-”, “”); } public static void main(String[] args) { while(true){ System。out。println(“uuid = ” + uuid()); try { Thread。sleep(1000); } catch (InterruptedException e) { e。printStackTrace(); } } }}

4。1 執行arthas

執行as。sh指令碼然後選擇要attach的java程序

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

4。2 大屏展示

執行緒、記憶體、GC、執行時資訊等,直接鍵入dashboard命令:

4。3 監控方法呼叫

test。TestCase的uuid方法呼叫的返回值

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

4。4 監控方法呼叫耗時

test。TestCase的uuid方法呼叫耗時

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

4。5 對方法的執行情況進行監控

對test。TestCase的uuid方法執行方法監控,返回結果包括:時間戳、類名、方法名、呼叫次數、成功次數、失敗次數、平均RT、失敗率

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

4。6 列印類的資訊

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

4。7 反編譯

反編譯test。TestCase類的uuid方法

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有

4。8 方法內部呼叫

輸出方法內部呼叫路徑,並輸出方法路徑上的每個節點上耗時

網際網路一線大廠常用的JVM效能監控與故障處理工具,你值得擁有