Java之JVM監控工具分享


Java之JVM監控工具分享

JVM的基本知識常用的也就是類加載機制內存區域、分配、OOMGCJVM參數調優

幾個鏈接自己看:

今天結合代碼講一講常用的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 則無法找到進程。

2. jstack

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

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

3. 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:垃圾回收消耗總時間

4. 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 的命令將無法執行。反過來說,如果你不想被其他進程監控,那么你需要開啟該參數。

5. 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

6. jinfo

功能 打印目標 Java 進程的配置參數
實時查看和調整虛擬機參數,可以顯示未被顯示指定的參數的默認值(jps -v 則不能)。

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

7. jcmd

可以用來實現前面除了jstat之外所有命令的功能。
詳見 :https://www.jianshu.com/p/388e35d8a09b

8. javap

javap 是一個能夠將 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();
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM