天天看點

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