JVM 之(9)虛擬機監控工具(可視化)

一、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 )
無認證鏈接 (下面的設置表示:鏈接的端口爲899九、無需認證就能夠被鏈接)
html

-Dcom.sun.management.jmxremote.port=8999 \  
-Dcom.sun.management.jmxremote.authenticate=false \  
-Dcom.sun.management.jmxremote.ssl=false  
    連接另外一臺計算機 
jconsole.exe 192.168.0.181:8999  

(3)概述
    包括:
        內存: 顯示內存使用信息
        線程: 顯示線程使用信息
        類: 顯示類裝載信息
        *VM摘要:*顯示java VM信息
        MBeans: 顯示 MBeans.


    下面使用內存、線程進行案例分析java


(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監控死鎖線程




二、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, 堆內存變大了shell

    在程序運行結束以前, 點擊Heap Dump 按鈕, 等待一下子,獲得dump結果,能夠看到一些Summary信息多線程

    點擊Classes, 發現char[]所佔用的內存是最大的

app

    雙擊它,獲得以下Instances結果jvm

    Instances是按Size由大到小排列的ide

    第一個就是最大的, 展開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時間最長, 以下圖:

相關文章
相關標籤/搜索