使用飛行記錄器監控Java Applications

1.Java 監控工具

Java 不僅僅是一種編程語言,而是一個非常豐富的生態系統,它有很多工具。JDK 包含的程序,允許我們編譯自己的程序,以及監視其狀態和 Java 虛擬機在程序執行的完整生命週期內的狀態。

JDK 提供的的 bin 文件夾包含可用於分析和監視的以下程序:

  • Java VisualVM (jvisualvm.exe)
  • JConsole (jconsole.exe)
  • Java Mission Control (jmc.exe)
  • Diagnostic Command Tool (jcmd.exe)

我們建議瀏覽此文件夾的內容,瞭解我們掌握的工具。

在本教程中,我們將重點介紹 Java 飛行記錄器。上述工具中不存在此,因爲它不是獨立程序。它的使用與上述兩個工具密切相關 - Java 任務控制和診斷命令工具。

2.基本概念

Java 飛行記錄器 (JFR) 是一種監視工具,用於在 Java 應用程序執行期間收集有關 Java 虛擬機 (JVM) 中事件的信息。JFR 是 JDK 分佈的一部分,並集成到 JVM 中。

JFR 旨在儘可能少地影響正在運行的應用程序的性能。

爲了使用 JFR,我們應該激活它。我們可以通過兩種方式實現此目的:

啓動Java應用程序時
在 Java 應用程序已運行時傳遞 jcmd 工具的診斷命令
JFR 沒有獨立工具。我們使用 Java 任務控制 (JMC),它包含一個插件,允許我們可視化 JFR 收集的數據。

這三個組件(JFR、jcmd 和 JMC)構成了一套完整的套件,用於收集正在運行的 Java 程序的低級運行時信息。在優化程序或診斷程序時,我們可能會發現此信息非常有用。

如果我們的計算機上安裝了各種版本的 Java,請務必確保 Java 編譯器 (javac)、Java 啓動器 (java) 和上述工具(JFR、jcmd 和 JMC)來自同一 Java 版本。否則,由於不同版本的 JFR 數據格式可能不兼容,因此存在無法看到任何有用數據的風險。

JFR 有兩個主要概念:事件和數據流。讓我們簡要地討論一下。

  1. Events

JFR 收集運行 Java 應用程序時在 JVM 中發生的事件。這些事件與 JVM 本身的狀態或程序的狀態相關。事件具有名稱、時間戳和其他信息(如線程信息、執行堆棧和堆的狀態)。

JFR 收集三種類型的事件:

  • 即時事件一旦發生,將立即記錄
  • 如果持續時間事件成功指定閾值,則記錄持續時間事件
  • 示例事件用於對系統活動進行採樣
  1. Dataflow

JFR 收集的事件包含大量數據。因此,根據設計,JFR 的速度足夠快,不會妨礙程序。

JFR 將有關事件的數據保存在單個輸出文件 flight.jfr 中。

正如我們所知,磁盤 I/O 操作相當昂貴。因此,JFR 使用各種緩衝區來存儲收集的數據,然後再將數據塊刷新到磁盤。事情可能會變得稍微複雜一點,因爲在同一時刻,一個程序可能有多個具有不同選項的註冊進程。

因此,我們可能會在輸出文件中找到比請求更多的數據,或者它可能不是按時間順序排列的。如果我們使用 JMC,我們甚至可能沒有注意到這一事實,因爲它按時間順序顯示事件。

在某些極少數情況下,JFR 可能無法刷新數據(例如,當事件過多或斷電時)。如果發生這種情況,JFR 會嘗試通知我們輸出文件可能缺少一段數據。

3.如何使用JFR

JFR 是一個實驗功能,因此其使用可能會發生變化。事實上,在早期的發行中,我們必須激活商業功能,以便在生產中使用。但是,從 JDK 11 開始,我們可能會使用它而不激活任何內容。我們始終可以查閱官方的 Java 發行說明,以檢查如何使用此工具。

對於 JDK 8,爲了能夠激活 JFR,我們應該使用選項 " +UnlockCommercialFeatures "和 " +FlightRecorder "啓動 JVM。

如上所述,有兩種方法可以激活 JFR。當我們在啓動應用程序時同時激活它時,我們從命令行執行。當應用程序已在運行時,我們使用診斷命令工具。

3.1 命令行

首先,我們使用標準java編譯器javac將程序的*.java文件編譯成一個*.class。

編譯成功後,我們可以使用以下選項啓動程序:

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder 
  -XX:StartFlightRecording=duration=200s,filename=flight.jfr path-to-class-file

其中到類文件的路徑是應用程序的入口點 _.class 文件。

此命令啓動應用程序並激活錄製,錄製將立即啓動,持續不超過 200 秒。收集的數據保存在輸出文件 flight.jfr 中。我們將在下一節中更詳細地介紹其他選項。

3.2 診斷命令行

使用jcmd工具

jcmd 1234 JFR.start duration=100s filename=flight.jfr

在 JDK 11 之前,爲了能夠以這種方式激活 JFR,我們應該使用未鎖定的商業功能啓動應用程序:

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -cp ./out/ com.baeldung.Main

應用程序運行後,我們使用其進程 ID 來執行各種命令,這些命令採用以下格式:

jcmd <pid|MainClass> <command> [parameters]

它的命令如下:

  • JFR.start – starts a new JFR recording
  • JFR.check – checks running JFR recording(s)
  • JFR.stop – stops a specific JFR recording
  • JFR.dump – copies contents of a JFR recording to file

每個命令都有一系列參數。例如,JFR.start 命令具有以下參數:

  • name – name of the recording; it serves to be able to reference this recording later with other commands
  • delay – dimensional parameter for a time delay of recording start, the default value is 0s
  • duration – dimensional parameter for a time interval of the duration of the recording; the default value is 0s, which means unlimited
  • filename – name of a file that contains the collected data
  • maxage – dimensional parameter for the maximum age of collected data; the default value is 0s, which means unlimited
  • maxsize – maximum size of buffers for collected data in bytes; the default value is 0, which means no max size

在本節的開頭,我們已經看到了這些參數的使用示例。有關參數的完整列表,我們可能始終參閱 Java 飛行記錄的官方文檔。

儘管 JFR 設計爲在 JVM 和應用程序的性能上儘可能少地佔用空間,但最好通過設置至少一個參數來限制收集數據的最大量:持續時間、最大長度或最大大小。

5.例子

我們的程序把對象插入list直到出現OutOfMemeryError異常。

public static void main(String[] args) {
    List<Object> items = new ArrayList<>(1);
    try {
        while (true){
            items.add(new Object());
        }
    } catch (OutOfMemoryError e){
        System.out.println(e.getMessage());
    }
    assert items.size() > 0;
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        System.out.println(e.getMessage());
    }
}

不用運行電腦,我們能發現一個缺點:這個while循環一直運行將導致CPU 飆高和內存佔用。我們使用JFR來分析一下。

5.2 開始註冊

用以下命令進行編譯:

javac -d out -sourcepath src/main src/main/com/baeldung/flightrecorder/FlightRecorder.java

*out/com/baeldung/flightrecorder* 目錄下會發現一個文件 *FlightRecorder.class*

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder 
  -XX:StartFlightRecording=duration=200s,filename=flight.jfr 
  -cp ./out/ com.baeldung.flightrecorder.FlightRecorder

5.3 可視化

img

在視圖的左側,我們看到"常規"、內存、代碼和線程等部分。每個部分包含包含詳細信息的各種選項卡。例如,"代碼"部分的選項卡"熱方法"包含方法調用的統計信息:

img

在此選項卡中,我們可以發現示例程序的另一個缺點:方法 java.util.Array.grow(int)已調用 17 次,以便在每次沒有足夠的空間來添加對象時放大數組容量。

在更現實的程序中,我們可能會看到許多其他有用的信息:

有關已創建對象的統計信息,當它們被垃圾回收器創建和銷燬時
關於線程的年表的詳細報告,當他們被鎖定或活動
應用程序正在執行的 I/O 操作。

參考代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章