1. 程式人生 > >[JVM]虛擬機器監控工具(視覺化)

[JVM]虛擬機器監控工具(視覺化)

1、Jconsole

            從Java 5開始 引入了 JConsole。JConsole 是一個內建 Java 效能分析器,可以從命令列或在 GUI shell 中執行。您可以輕鬆地使用 JConsole(或者,它更高階的 “近親” VisualVM )來監控 Java 應用程式效能和跟蹤 Java 中的程式碼。  

(1)啟動

        目錄在 jdk\bin\jconsole.exe ,雙擊啟動            配置jdk環境變數後,命令列Jconsole.exe執行

(2)連結配置

    本地程式(相對於開啟JConsole的計算機),無需設定任何引數就可以被本地開啟的JConsole連線(Java SE 6開始無需設定,之前還是需要設定執行時引數 -Dcom.sun.management.jmxremote ) 無認證連線 (下面的設定表示:連線的埠為8999、無需認證就可以被連線)

  1. -Dcom.sun.management.jmxremote.port=8999 \

  2. -Dcom.sun.management.jmxremote.authenticate=false \

  3. -Dcom.sun.management.jmxremote.ssl=false

    連結另一臺計算機 

jconsole.exe 192.168.0.181:8999  

(3)概述

    包括:         記憶體: 顯示記憶體使用資訊         執行緒: 顯示執行緒使用資訊         類: 顯示類裝載資訊         *VM摘要:*顯示java VM資訊         MBeans: 顯示 MBeans.

    下面使用記憶體、執行緒進行案例分析

(4)記憶體分析

public class JconsoleHeapTest {

    public static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {
        try {
            Thread.sleep(5000);
        }catch (Exception e){

        }
        System.out.println("method start ====>");
        fill(1000);
        System.out
.println("<==== method end "); } private static void fill(Integer cnt){ List<JconsoleHeapTest> jconsoleTests = new ArrayList<>(); for(int i = 0; i<cnt; i++){ try { Thread.sleep(100); }catch (Exception e){ } jconsoleTests.add(new JconsoleHeapTest()); } } }

    執行程式,建立連結     使用Prarllel GC 收集器,觀測 堆記憶體和新生代Eden 記憶體使用持續增加  

(5)執行緒分析

public class JconsoleThreadTest {

    public static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        scanner.next();

        new Thread(() -> {
            System.out.println("start whileThread ===>");
            while (true){

            }
        }, "whileThread").start();

        scanner.next();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("start watiThread ===>");
                Object o = new Object();
                synchronized (o){
                    try {
                        o.wait();
                    }catch (Exception e){

                    }
                }
            }
        }, "watiThread").start();
    }
}

    建立連結後,首先觀察main執行緒,一直在執行狀態

    控制檯輸入next,後 執行whilleTread執行緒  

    控制檯再次輸入next,後 執行waitTread執行緒  

(6)執行緒死鎖分析監控

public class JconsoleSyncDeadLockTest {

    private static Object locka = new Object();
    private static Object lockb = new Object();

    public static void main(String[] args){
        new JconsoleSyncDeadLockTest().deadLock();
    }

    private void deadLock(){
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (locka){
                    try{
                        System.out.println(Thread.currentThread().getName()+" get locka ing!");
                        Thread.sleep(500);
                        System.out.println(Thread.currentThread().getName()+" after sleep 500ms!");
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+" need lockb!Just waiting!");
                    synchronized (lockb){
                        System.out.println(Thread.currentThread().getName()+" get lockb ing!");
                    }
                }
            }
        },"thread1");

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockb){
                    try{
                        System.out.println(Thread.currentThread().getName()+" get lockb ing!");
                        Thread.sleep(500);
                        System.out.println(Thread.currentThread().getName()+" after sleep 500ms!");
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+" need locka! Just waiting!");
                    synchronized (locka){
                        System.out.println(Thread.currentThread().getName()+" get locka ing!");
                    }
                }
            }
        },"thread2");

        thread1.start();
        thread2.start();
    }
}

    執行後,兩個執行緒相互等待,發生死鎖     Jconsole監控死鎖執行緒  

 

2、VisualVM

    VisualVM 是一款免費的,集成了多個 JDK 命令列工具的視覺化工具,它能為您提供強大的分析能力,對 Java 應用程式做效能分析和調優。這些功能包括生成和分析海量資料、跟蹤記憶體洩漏、監控垃圾回收器、執行記憶體和 CPU 分析,同時它還支援在 MBeans 上進行瀏覽和操作。本文主要介紹如何使用 VisualVM 進行效能分析及調優。     參考:http://www.cnblogs.com/wade-xu/p/4369094.html

(1)記憶體堆Heap

public final static int OUTOFMEMORY = 200000000;

private String oom;

private int length;

StringBuffer tempOOM = new StringBuffer();

public JavaHeapTest(int leng) {
    this.length = leng;

    int i = 0;
    while (i < leng) {
        i++;
        try {
            tempOOM.append("a");
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            break;
        }
    }
    this.oom = tempOOM.toString();

}

public String getOom() {
    return oom;
}

public int getLength() {
    return length;
}

public static void main(String[] args) {
    JavaHeapTest javaHeapTest = new JavaHeapTest(OUTOFMEMORY);
    System.out.println(javaHeapTest.getOom().length());
}

    檢視VisualVM Monitor tab, 堆記憶體變大了

    在程式執行結束之前, 點選Heap Dump 按鈕, 等待一會兒,得到dump結果,可以看到一些Summary資訊

    點選Classes, 發現char[]所佔用的記憶體是最大的

    雙擊它,得到如下Instances結果

    Instances是按Size由大到小排列的

    第一個就是最大的, 展開Field區域的 values

    StringBuffer型別的 全域性變數 tempOOM 佔用記憶體特別大, 注意區域性變數是無法通過 堆dump來得到分析結果的。

    另外,對於“堆 dump”來說,在遠端監控jvm的時候,VisualVM是沒有這個功能的,只有本地監控的時候才有。  

(2) 執行緒分析

    Java 語言能夠很好的實現多執行緒應用程式。當我們對一個多執行緒應用程式進行除錯或者開發後期做效能調優的時候,往往需要了解當前程式中所有執行緒的執行狀態,是否有死鎖、熱鎖等情況的發生,從而分析系統可能存在的問題。

    在 VisualVM 的監視標籤內,我們可以檢視當前應用程式中所有活動執行緒(Live threads)和守護執行緒(Daemon threads)的數量等實時資訊。

public static void main(String[] args) {

    MyThread mt1 = new MyThread("Thread a");
    MyThread mt2 = new MyThread("Thread b");

    mt1.setName("My-Thread-1 ");
    mt2.setName("My-Thread-2 ");

    mt1.start();
    mt2.start();
}

public MyThread(String name) {
}

public void run() {

    while (true) {

    }
}

    Live threads 從11增加兩個 變成13了

    Daemon threads從8增加兩個 變成10了 

    VisualVM 的執行緒標籤提供了三種檢視,預設會以時間線的方式展現, 如下圖:

    可以看到兩個我們run的程式裡啟的執行緒:My-Thread-1 和 My-Thread-2

另外還有兩種檢視分別是表檢視和詳細資訊檢視, 這裡看一下每個Thread的詳細檢視:

(3)CPU 分析  

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

    cpuFix();
}


/**
 * cpu 執行固定百分比
 *
 * @throws InterruptedException
 */
public static void cpuFix() throws InterruptedException {
    // 80%的佔有率
    int busyTime = 8;
    // 20%的佔有率
    int idelTime = 2;
    // 開始時間
    long startTime = 0;

    while (true) {
        // 開始時間
        startTime = System.currentTimeMillis();
        
        /*
         * 執行時間
         */
        while (System.currentTimeMillis() - startTime < busyTime) {
            ;
        }

        // 休息時間
        Thread.sleep(idelTime);
    }
}

    檢視監視頁面 Monitor tab

    過高的 CPU 使用率可能是由於我們的專案中存在低效的程式碼;

    在我們對程式施壓的時候,過低的 CPU 使用率也有可能是程式的問題。

    點選取樣器Sampler, 點選“CPU”按鈕, 啟動CPU效能分析會話,VisualVM 會檢測應用程式所有的被呼叫的方法,

    在CPU samples tab 下可以看到我們的方法cpufix() 的自用時間最長, 如下圖:

    切換到Thread CPU Time 頁面下,我們的 main 函式這個程序 佔用CPU時間最長, 如下圖: