JDK的可視化工具系列 (四) JConsole、VisualVM


JConsole: Java監視與管理控制台

代碼清單1:

import java.util.*;

public class JConsoleDemo {
    
    static class OOMObject {
        public byte[] placeholder = new byte[64 * 1024];
    }

    public static void fillHeap(int num) throws InterruptedException {
        List<OOMObject> list = new ArrayList<OOMObject>();
        for (int i = 0; i < num; i++) {
            Thread.sleep(50);
            list.add(new OOMObject());
        }
        System.gc(); 
    }

    public static void main(String[] args) throws Exception {
        fillHeap(1000);
        //System.gc();
        Thread.sleep(10000);
    }
}

內存監控:

編譯運行JConsoleDemo類, 運行時設置的虛擬機參數為 -Xms100m -Xmx100m -XX:+UseSerialGC , 在%JAVA_HOME%\bin目錄下, 啟動jconsole.exe , 將自動搜索出本機運行的所有虛擬機進程, 這里我們選擇JConsoleDemo對應的進程2464。

啟動后主界面如下: 

在"內存"頁簽, 查看堆內存Eden區的運行趨勢如下:

從圖中詳細信息可以看出, Eden區的內存大小為27.328KB, 所以折線圖中顯示每次到27Mb左右時系統就會進行一次GC。當1000次循環結束后, 執行System.gc(), 柱狀圖中顯示Eden區和Survivor區基本被清空, 但老年代的對應柱狀圖仍保持峰值狀態, 這是因為System.gc()是在fillHeap()方法內執行, 所以list對象在System.gc()執行時仍然是存活的( 處於作用域之內、被引用)。如果將System.gc()移動到fillHeap()方法外執行, 如下柱狀圖所示, 則會回收包括老年代的所有內存。

代碼清單2:

import java.io.*;

public class JConsoleDemo2 {
    
    static class SynAddRunalbe implements Runnable {
        int a, b;
        public SynAddRunalbe(int a, int b) {
            this.a = a;
            this.b = b;
        }

        @Override
        public void run() {
            synchronized (Integer.valueOf(a)) {
                synchronized (Integer.valueOf(b)) {
                    System.out.println(a + b);
                }
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new SynAddRunalbe(1, 2)).start();
        new Thread(new SynAddRunalbe(2, 1)).start();
    }
}

線程監控:

編譯運行, 在"線程"頁簽可查看"死鎖"描述。這是因為1、2兩個數值在Integer類的緩存常量池[-128, 127]范圍內, 這樣當多次調用Integer.valueOf()方法時, 不會再每次都創建對象, 而是直接返回緩存常量池中的對象。所以上面兩個線程的同步代碼塊中實際上只創建了兩個鎖對象, 且在某一時刻互相持有對方的鎖, 即"死鎖"現象。

VisualVM: 多合一故障處理工具

概述與插件安裝

VisualVM基於NetBeans平台開發, 因此它一開始就具備了插件擴展的特性, 通過插件支持, VisualVM可以做許多事情, 例如:

  • 顯示虛擬機進程和進程的配置、環境信息(jps、jinfo)
  • 監視應用程序的CPU、GC、堆、方法區及線程的信息(jstat、jstack)
  • dump及分析堆轉儲快照(jmap、jhat)
  • 方法級的程序運行性能分析, 找出被調用最多、運行時間最長的方法
  • 離線程序快照: 收集程序的運行時配置、線程dump、內存dump等信息建立一個快照, 可以將快照發送開發者處進行bug反饋等等

%JAVA_HOME%\bin目錄下, 啟動jvisualvm.exe進入主界面, 點擊"工具"→"插件"→"可用插件"選項, 選擇所需的插件安裝。

安裝好插件后, 選擇一個正在運行的java程序就可以查看程序監控的主界面了

堆轉儲快照

兩種方式生成堆dump文件: 

  • 在"應用程序"窗口中右鍵單擊應用程序節點, 選擇"堆 Dump"
  • 在"監視"頁簽中選擇"堆 Dump"

分析程序性能

在Profiler頁簽中, 可以對程序運行期間方法級的CPU和內存進行分析, 這個操作會對程序運行性能有很大影響, 所以一般不再生產環境使用。CPU分析將會統計每個方法的執行次數、執行耗時; 內存分析則會統計每個方法關聯的對象數及對象所占空間。

BTrace動態日志跟蹤

BTrace既可以作為visualVM的插件來使用, 也可以獨立運行。它能在不停止目標程序運行的前提下, 通過熱部署技術加入原本並不存在的調試代碼, 以實現對程序的動態調試。

上面我們已經安裝好了BTrace插件, 這里編譯運行BTraceTest。

import java.io.*;

public class BTraceTest {

    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) throws IOException {
        BTraceTest test = new BTraceTest();
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        for (int i = 0; i < 10; i++) {
            reader.readLine();
            int a = (int) Math.round(Math.random() * 1000);
            int b = (int) Math.round(Math.random() * 1000);
            System.out.println(test.add(a, b));
        }
    }
}

在"應用程序"頁簽中選擇對應的java進程, 右鍵選擇"Trace Application", 進入BTrace面板:

在BTrace頁簽內輸入調試代碼:

/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class TracingScript {
    @OnMethod(
    clazz="BTraceTest",
    method="add",
    location=@Location(Kind.RETURN)
)

public static void func(@Self BTraceTest instance,int a,int b,@Return int result) {
    println("調用堆棧:");
    jstack();
    println(strcat("方法參數A:",str(a)));
    println(strcat("方法參數B:",str(b)));
    println(strcat("方法結果:",str(result)));
}
}

點擊BTrace面板的"Start"按鈕, 對程序進行動態調試:

BTrace不僅可以用來打印調用堆棧、參數、返回值, 還可以進行性能監控、定位連接泄漏和內存泄漏、解決多線程競爭問題, 這里我們只是做一個入門的了解, 具體的拓展應用以后會專門用一篇文章來講解。

參考資料

《深入理解Java虛擬機》

作者:張小凡
出處:https://www.cnblogs.com/qingshanli/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】。


免責聲明!

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



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