Java之JVM監控工具分享

Java之JVM監控工具分享
VM的基本知識常用的也就是類加載機制,內存區域、分配、OOM,GC,JVM參數調優

幾個鏈接自己看:

內存區域&類加載機制
分配策略&垃圾回收算法、收集器
今天結合代碼講一講常用的java自帶工具講解,這些命令一般都是jdk/lib/tools.jar中。用來監控診斷我們的Java環境。

官方說明: https://docs.oracle.com/en/java/javase/11/tools/

  1. jps
    顯示當前用戶的所有java進程的PID 以及主類名

jps : 顯示當前用戶的所有java進程的PID 以及主類名
jps -v : 打印傳遞給 Java 虛擬機的參數(如-XX:+UnlockExperimentalVMOptions -XX:+UseZGC)
jps -m : 打印傳遞給主類的參數
jps -l : 打印模塊名以及包名
默認開啓(UsePerfData),若加上-XX:-UsePerfData 則無法找到進程。

  1. jstack
    功能 jstack不僅會打印線程的棧軌跡、線程狀態(BLOCKED)、持有的鎖(locked…)以及正在請求的鎖(waiting to lock …),而且還會分析出具體的死鎖。

jstack pid : 查看線程情況
jstack -F pid : 正常輸出不被響應時,使用該指令
jstack -l pid : 除堆棧外,顯示關於鎖的附件信息

  1. jstat
    功能 允許用戶查看目標 Java 進程的類加載、即時編譯以及垃圾回收相關的信息。它常用於檢測垃圾回收GC問題以及內存泄漏問題。

顯示進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據。
常用指令

jstat -class pid : 打印類裝載、類卸載、總空間以及所消耗的時間
jstat -compiler pid : 打印即時編譯相關的數據
jstat -printcompilation pid : 打印即時編譯相關的數據
jstat -gc pid 1s 20 : 查詢垃圾收集情況,每1秒查詢一次,一共查詢20次。
jstat -gccause pid : 額外輸出上次GC原因
...剩下的都是以-gc爲前綴的子命令,它們將打印垃圾回收相關的數據。
加上 -t參數 每行數據之前打印目標 Java 進程的啓動時間
我們可以看到,這兩個 Survivor 區的容量相等,而且始終有一個 Survivor 區的內存使用量爲 0。
在這種情況下,Java 虛擬機會將這塊內存區域回收,並標記爲可分配的狀態。這樣子做的結果是,堆中可能完全沒有 Survivor 內存區域,因而相應的 S1C 和 S1U 將會是 0。

我們可以比較 Java 進程的啓動時間以及總 GC 時間(GCT 列),或者兩次測量的間隔時間以及總GC時間的增量,來得出 GC 時間佔運行時間的比例。
如果該比例超過 20%,則說明目前堆的壓力較大;如果該比例超過 90%,則說明堆里幾乎沒有可用空間,隨時都可能拋出 OOM 異常。

jstat還可以用來判斷是否出現內存泄漏。在長時間運行的 Java 程序中,我們可以運行jstat命令連續獲取多行性能數據,並取這幾行數據中 OU 列(即已佔用的老年代內存)的最小值。
然後,我們每隔一段較長的時間重複一次上述操作,來獲得多組 OU 最小值。如果這些值呈上漲趨勢,則說明該 Java 程序的老年代內存已使用量在不斷上漲,這意味着無法回收的對象在不斷增加,因此很有可能存在內存泄漏。

CGC 和 CGCT,它們分別代表併發 GC Stop-The-World 的次數和時間。

S0C:年輕代中第一個survivor(倖存區)的容量 (kb)
S1C:年輕代中第二個survivor(倖存區)的容量 (kb)
S0U:年輕代中第一個survivor(倖存區)目前已使用空間 (kb)
S1U:年輕代中第二個survivor(倖存區)目前已使用空間 (kb)
EC:年輕代中Eden(伊甸園)的容量 (kb)
EU:年輕代中Eden(伊甸園)目前已使用空間 (kb)
OC:老年代的容量 (kb)
OU:老年代目前已使用空間 (kb)
MC:元空間的容量 (kb)
MU:元空間目前已使用空間 (kb)
CCSC:壓縮類的容量 (kb)
CCSU:壓縮類目前已使用空間 (kb)
YGC:年輕代垃圾回收次數
YGCT:年輕代垃圾回收消耗時間
FGC:老年代垃圾回收次數
FGCT:老年代垃圾回收消耗時間
GCT:垃圾回收消耗總時間

  1. jmap
    功能 生成堆轉儲快照(heapdump) 用戶統計目標 Java 進程的堆中存放的 Java 對象,並將它們導出成二進制文件。查詢Java堆和永久代的詳細信息,使用率,使用大小,查詢finalize執行隊列的信息

常用指令

jmap -heap pid : 打印jvm heap的情況
jmap -histo pid : 打印jvm heap的直方圖。其輸出信息包括類名,對象數量,對象佔用大小。 並按照內存使用量從多至少的順序排列
jmap -histo:live pid : JVM會先觸發gc,然後再統計信息,只統計堆中的存活對象的情況
jmap -dump:format=b,file=map.log pid: 將內存使用的詳細情況輸出到文件,之後一般使用其他工具進行分析。同樣,-dump:live只保存堆中的存活對象。
jmap -clstats pid : 打印被加載類的信息
jmap -finalizerinfo pid : 該子命令將打印所有待 finalize 的對象。
jmap -permstat pid : 打印permanent generation heap情況
我們通常會利用jmap -dump:live,format=b,file=filename.bin命令,將堆中所有存活對象導出至一個文件之中。
這里format=b將使jmap導出與hprof(在 Java 9 中已被移除)、-XX:+HeapDumpAfterFullGC、-XX:+HeapDumpOnOutOfMemoryError格式一致的文件。這種格式的文件可以被其他 GUI 工具查看。

jmap(以及jinfo、jstack和jcmd)依賴於 Java 虛擬機的Attach API,因此只能監控本地 Java 進程。
一旦開啓 Java 虛擬機參數DisableAttachMechanism(即使用參數-XX:+DisableAttachMechanism),基於 Attach API 的命令將無法執行。反過來說,如果你不想被其他進程監控,那麼你需要開啓該參數。

  1. jhat
    功能 一般與jmap搭配使用,用來分析jmap生成的堆轉儲文件。

由於有很多可視化工具(Eclipse Memory Analyzer 、IBM HeapAnalyzer)可以替代,所以很少用。不過在沒有可視化工具的機器上也是可用的。
常用指令

jmap -dump:format=b,file=map.log pid : 將內存使用的詳細情況輸出到文件
jhat map.log : 解析Java堆轉儲文件,並啓動一個 web server
演示:https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html

  1. jinfo
    功能 打印目標 Java 進程的配置參數

實時查看和調整虛擬機參數,可以顯示未被顯示指定的參數的默認值(jps -v 則不能)。

jinfo pid :可用來查看目標 Java 進程的參數,如傳遞給 Java 虛擬機的-X(即輸出中的 jvm_args)、-XX參數(即輸出中的 VM Flags),以及可在 Java 層面通過System.getProperty獲取的-D參數(即輸出中的 System Properties)。

  1. jcmd
    可以用來實現前面除了jstat之外所有命令的功能。

詳見 :https://www.jianshu.com/p/388e35d8a09b

  1. javap
  2. 是一個能夠將 class 文件反彙編成人類可讀格式的工具。 ASM字節碼操作:https://blog.csdn.net/ohcezzz/article/details/78416176

默認情況下 javap 會打印所有非私有的字段和方法,
-p 打印私有的字段和方法。
-v 打印所有信息。
-c 查閱方法對應的字節碼
附:
一隻懂JVM參數的狐狸

代碼驗證

@Slf4j
public class JvmTest {

private byte[] memory;

public JvmTest(byte[] memory) {
    this.memory = memory;
}

public static void main(String[] args) throws InterruptedException {

    GC測試();
    //死循環();
    //死鎖();
}

public static void GC測試() throws InterruptedException {
    for (int i = 1; i < 5; i++) {
        byte[] b = new byte[50 * 1024 * 1024];
        log.info("分配了50M空間給數組");
        Thread.sleep(10000);
    }
    //方法區中常量引用對象 (虛擬機棧(棧幀中的局部變量)中引用的對象 - 方法區中的靜態變量引用的對象 - 本地方法棧中JNI(即一般說的Native方法)中引用的對象)
    JvmTest jvmTest = new JvmTest(new byte[50 * 1024 * 1024]);
    log.info("分配了50M空間給對象");
    log.info("調用了System.gc()");
    System.gc();
    jvmTest = null;
    Thread.sleep(10000);
    log.info("調用了System.gc()");
    System.gc();
    Thread.sleep(3000000);
}

public static void 死循環() {
    while (true) {
    }
}

public static void 死鎖() {
    String obj1 = "obj1";
    String obj2 = "obj2";
    Runnable r1 = () -> {
        log.info("r1 running");
        while (true) {
            synchronized (obj1) {
                log.info("r1 lock obj1");
                try {
                    //獲取obj1後先等一會兒,讓Lock2有足夠的時間鎖住obj2
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj2) {
                    log.info("r1 lock obj2");
                }
            }
        }
    };
    Runnable r2 = () -> {
        log.info("r2 running");
        while (true) {
            synchronized (obj2) {
                log.info("r2 lock obj2");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj1) {
                    log.info("r2 lock obj1");
                }
            }
        }
    };
    Thread a = new Thread(r1);
    Thread b = new Thread(r2);
    a.start();
    b.start();
}

}
原文地址 https://www.cnblogs.com/loveincode/p/10577550.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章