在前面已經學習了JVM性能監控的命令行工具,接下來學習JVM性能監控的命令行工具,通過可視化工具可以更直觀地監控JVM性能、處理JVM相關問題。
1、JConsole
JConsole( Java Monitoring and Management Console),是一款基於 JMX( Java Manage-ment Extensions) 的可視化監視管理工具。
它的功能主要是對系統進行收集和參數調整,不僅可以用在虛擬機本身的管理上,還可以用於運行於虛擬機之上的軟件中。
1.1、JConsole連接Java程序
JConsole程序位於%JAVA_HOME%bin目錄下,直接通過命令啟動。
在新建連接對話框中,羅列了所有的本地Java應用程序,選擇需要連接的程序即可。
下面還有一個用於連接遠程進程的文本框,輸入正確的遠程地址即可連接。
如果一個程序需要使用JConsole與那成連接,則需要在啟動Java程序時,加上以下參數:
JAVA_OPTS="-Dfile.encoding=UTF-8"
JAVA_OPTS="$JAVA_OPTS -Dlog.dir=$LOG_PATH"
JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=xxx.xxx.xxx.xxx(本機IP) -Dcom.sun.management.jmxremote"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=xx"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.local.only=false"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"
1.2、Java程序概況
使用JConsole連接了一個本地程序,在概述
可以看到Java程序運行的概覽信息,包括堆內存使用情況
、線程
、類
、CPU使用情況
四項信息的曲線圖。
1.3、內存監控
內存
的作用相當於可視化的jstat命令,用於監視被收集器管理的虛擬機內存。
它不僅包含堆內存的整體信息,更細化到eden區、suvivior區、老年代的使用情況。
為了更加清晰地查看內存地變化,運行下面一段程序,然后連接:
/**
* VM參數: -Xms100m -Xmx100m -XX:+UseSerialGC
*/
public class JConcoleRAMMonitor {
/***
* 內存占位符對象,一個OOMObject大約占64KB
*/
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(300);
list.add(new OOMObject());
}
System.gc();
}
public static void main(String[] args) throws Exception {
fillHeap(2000);
}
}
這段代碼的作用是以64KB/50ms的速度向Java堆中填充數據,一共填充1000次。
觀察Eden區的運行趨勢,發現呈折線。觀察堆內存使用,發現以稍有曲折的狀態向上增長。
執行System.gc()之后,老年代的柱狀圖仍然顯示峰值狀態,最后程序會以堆內存溢出結束,這是因為空間未能回收——List<OOMObject>list對象一直存活, fillHeap()方法仍然沒有退出,如果把 System.gc()移動到fillHeap()方法外調用就可以回收掉全部內存。
1.4、線程監控
JConcole還可以監控線程,相當於可視化的jstack命令。如圖,JConcole顯示了系統內的線程數量,並在屏幕下方顯示了程序中所有的線程。單擊線程名稱,就可以查看線程的棧信息。
使用JConsole還可以快速定位死鎖問題。
這是一段會產生死鎖的代碼:
public class ThreadLockDemo {
/**
* 線程死鎖等待演示
*/
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) {
for (int i = 0; i < 100; i++) {
new Thread(new SynAddRunalbe(1, 2)).start();
new Thread(new SynAddRunalbe(2, 1)).start();
}
}
}
出現線程死鎖以后,點擊JConsole線程面板的檢測到死鎖
按鈕,將會看到線程的死鎖信息。
可以看到線程Thread-199等待線程Thread-21持有的資源。
1.5、類加載情況
如圖,類頁面顯示了已經裝載的類數量。在詳細信息欄中,還顯示了已經卸載的類的數量。
1.6、虛擬機信息
在VM摘要
,JConsole顯示了當前應用程序的運行環境,包括虛擬機類型、版本、堆信息以及虛擬機參數等。
2、VisualVM
VisualVM(All-in-One Java Troubleshooting Tool)是功能最強大的運行監視和故障處理程序之一,曾經在很長一段時間內是Oracle官方主力發展的虛擬機故障處理工具。
相比一些第三方工具,VisualVM有一個很大的優點:不需要被監視的程序基於特殊Agent去運行,因此它的通用性很強,對應用程序實際性能的影響也較小,使得它可以直接應用在生產環境中。
2.1、VisualVM安裝插件
在JDK6 Update7以后,VisualVM便作為JDK的一部分發布,它在%JAVA_HOME%bin 目錄下,點擊就可以啟動。
VisualVM的精華之處在於它的插件。插件安裝可以手動安裝或者自動安裝。
手動安裝,從地址 https://visualvm.github.io/pluginscenters.html 下載載nbm包,點擊“工具->插件->已下載”菜單,然后在彈出對話框中指定nbm包路徑便可完成安裝。
一般選擇自動安裝,點擊工具
-> 插件菜單
,在可用插件
里可以看到可安裝的插件,按需安裝即可。
VisualVM中概述
,監視
、線程
,MBeans
的功能與前面介紹的JConsole差別不大,這里就不在贅言。
2.2、生成、瀏覽堆轉儲快照
在VisualVM中生成堆轉儲快照文件有兩種方式,可以執行下列任一操作:
- 在
應用程序
窗口中右鍵單擊應用程序節點,然后選擇堆Dump
。 - 在
應用程序
窗口中雙擊應用程序節點以打開應用程序標簽,然后在“監視”標簽中單擊堆Dump
。
生成堆轉儲快照文件之后,該堆的應用程序下增加了一個以[heap-dump]開頭的子節點。如果需要把堆轉儲快照保存或發送出去,就需要heapdump節點上右鍵選擇“另存為”菜單,否則當VisualVM關閉時,生成的堆轉儲快照文件會被當作臨時文件自動清理掉。要打開一個由已經存在的堆轉儲快照文件,通過文件菜單中的“裝入”功能,選擇磁盤上的文件即可。
2.3、分析程序性能
要開始性能分析,先選擇“CPU”和“內存”按鈕中的一個,然后切換到應用程序中對程序進行操作,VisualVM會記錄這段時間中應用程序執行過的所有方法。
如果是進行處理器執行時間分析,將會統計每個方法的執行次數、執行耗時;
如果是內存分析,則會統計每個方法關聯的對象數以及這些對象所占的空間。
等要分析的操作執行結束后,點擊“停止”按鈕結束監控過程。
2.4、BTrace動態日志跟蹤
BTrace是個很有意思的插件,它可以在不停機的情況下,通過字節碼注入動態監控系統的運行情況。
Btrace自動安裝如下,到github的網絡可能存在不穩定的問題,可以重試,或者手動安裝
在VisualVM中安裝了BTrace插件后,在應用程序面板中右擊要調試的程序,會出現“Trace Application…”菜單:
點擊將進入BTrace面板。這個面板看起來就像一個簡單的Java程序開發環境:
現在來嘗試使用BTrace追蹤正在運行的程序。
一段簡單的Java代碼:產生兩個1000以內的隨機整數,輸出這兩個數字相加的結果。
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));
}
}
}
運行程序,現在需要在不停止程序的情況下,監控程序中生成的兩個隨機數。在VisualVM中打開該程序的監視,在BTrace頁 簽填充TracingScript的內容,輸入調試代碼:
/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class TracingScript {
@OnMethod(clazz = "cn.fighter3.test.BTraceTest",
method = "add",
location = @Location(Kind.RETURN)
)
public static void func(@Self cn.fighter3.test.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)));
}
}
點擊start按鈕,當程序運行時將會在Output面板輸出調試信息:
BTrace的用途很廣泛,打印調用堆棧、參數、返回值只是它最基礎的使用形式,更多應用可以查看官方倉庫 https://github.com/btraceio/btrace/wiki 。
3、Java Mission Control
JMV最初是JRockit虛擬機提供的一款診斷工具。在Oracle JDK7 Update 40以后,它就綁定在Oracle JDK中發布。
JMC位置是%JAVA_HOME%/bin/jmc.exe
,打開軟件界面:
在左側的“JVM瀏覽器”面板中自動顯示了通過JDP協議(Java Discovery Protocol)找到的本機正在運行的HotSpot虛擬機進程。
3.1、MBean服務器
點擊本地進程的MBean服務器
:
可以看到,以飛行儀表的視圖顯示了Java堆使用率,CPU使用率和Live Set+Fragmentation。
3.2、飛行記錄器(Flight Recorder)
飛行記錄器是JMC提供的另一大功能,它通過記錄程序在一段時間內的運行情況,將記錄結果進行分析和展示,可以更進一步對系統的性能進行分析和診斷。
要使用JFR,程序啟動需要帶以下參數:
-XX:+UnlockCommercialFeatures -XX:+FlightRecorder
連接加了相關參數啟動的程序,啟動飛行記錄,進行一分鍾的性能記錄:
記錄結束后,JMC會自動打開剛才的記錄:
JFR提供的數據質量通常也要比其他工具通過代理形式采樣獲得或者從MBean中取得的數據高得多。以垃圾搜集為例,HotSpot的MBean中一般有各個分代大小、收集次數、時間、占用率等數據(根據收集器不同有所差別),這些都屬於“結果”類的信息,而JFR中還可以看到內存中這段時間分配了哪些對象、哪些在TLAB中(或外部)分配、分配速率 和壓力大小如何、分配歸屬的線程、收集時對象分代晉升的情況等。
4、第三方工具
以上三個都是JDK自帶的性能監控工具,除此之外還有一些第三方的性能監控工具。
- MAT
Java 堆內存分析工具。
- GChisto
GC 日志分析工具。
- GCViewer
GC
日志分析工具。
- JProfiler
商用的性能分析利器。
- arthas
阿里開源診斷工具。
- async-profiler
Java 應用性能分析工具,開源、火焰圖、跨平台。
這里只是簡單羅列,就不再展開詳細介紹。
參考:
【1】:周志明編著《深入理解Java虛擬機:JVM高級特性與最佳實踐》
【2】:《實戰JAVA虛擬機 JVM故障診斷與性能優化》