1. 程式人生 > >Java Mission Control-Java 效能分析工具

Java Mission Control-Java 效能分析工具

引言

本文為 Java 效能分析工具系列文章第三篇,這裡將介紹如何使用 Java 任務控制器 Java Mission Control 深入分析 Java 應用程式的效能,為程式開發人員在使用 Java 任務控制器的時候提供幫助。第一篇:作業系統工具第二篇:Java 內建監控工具

JMC 是在 JAVA 7u40 釋出中加入的效能監控工具。使用過 JDK 6 中 JRockit JVM 的使用者並不會陌生,因為它是 Java 7 中 JMC 功能的一部分。啟動 JMC 後將會顯示當前機器中的所執行的 JVM 程序資訊,當然我們也可以選擇新增更多的 JVM 程序進行監控。圖 1 中展示了使用 JMC 監控 GlassFish 應用伺服器的畫面。圖中展示了被監控程式的基本資訊,其包括 CPU 使用率和記憶體堆的使用率,值得注意的是,JMC 監控圖中顯示的是當前機器的 CPU 的使用情況,可以看到的是 JMC 監控的是整個系統,而並非只是被選中的 JVM 對 CPU 的使用情況。通過自定義設定上方儀表盤中顯示的資訊,既可以檢視 被監控 JVM 的詳細資訊,例如垃圾回收,類的載入,執行緒的使用,以及記憶體堆的使用率,等等。也可以檢視指定作業系統資訊,例如系統的 CPU 和記憶體的使用率,磁碟的交換資訊,平均負載等相關資訊。

圖 1. Java Mission Control 監控圖

圖 1. Java Mission Control 監控圖

回頁首

JFR (Java Flight Recorder)

JFR 是 JMC 中一個非常關鍵的功能。它記錄了 JVM 所有事件的歷史資料,通過這些資料,程式效能分析人員可以結合以往的歷史資料對 JVM 效能瓶頸進行分析診斷。

JFR 的基本操作是開啟一系列的事件(例如,一個執行緒為了等待鎖而被阻塞)。當某個事件發生時,這個事件的所有資料將會被儲存至記憶體或者一個檔案當中。資料流被保留在一個環形快取中,所以只有最近發生的事件的資料才是可用的。JMC 可以從 JVM 或者檔案中讀取並展示這些事件資料,通過這些資料,可以進行效能分析。

通過 JVM 引數,JMC 使用者介面以 jcmd 命令,可以指定上文中提到的事件型別、環形快取的大小、資料儲存的位置等資訊。在預設設定下,JMC 對被監控應用的影響非常小。但是隨著越來越多的事件被開啟,以及觸發事件的閥值的改變,JMC 的影響也在變化。接下來將展示 JFR 的使用者介面。

JFR 概述

這個例子使用 JFR 記錄 GlassFish 應用伺服器的 6 分鐘內的相關資料。圖 2 展示了 JMC 將這些資料載入之後的到總體檢視。

圖 2. JMC 資料載入示例圖

圖 2. JMC 資料載入示例圖

這張圖上的資訊類似於 JMC 基本資訊監控所看到的畫面。上面的儀表盤顯示的是 CPU 使用率和記憶體堆使用率,在其上方有一條標示事件順序的時間軸,垂直柱狀體代表事件。可以放大時間軸中感興趣的部分進行詳細分析,就像圖中所示,從 6 分鐘的時間軸選出位於末端的 1 分 6 秒進行放大。

圖 2 中 CPU 使用率曲線非常清楚的看到,GlassFish 伺服器所在 JVM 平均大約 70%的使用率,而機器整體的 CPU 使用率達到 100%。在最下端,有一些標籤可以選擇,系統屬性標籤,JVM 資訊標籤等,在畫面左側的按鈕提供更加詳細的應用程式執行狀況。

JFR 記憶體檢視

JFR 記憶體檢視收集的資訊非常豐富,下圖只展示了其中的一部分。從圖 3 中可以看出,新生代(Young Generation) 佔用的記憶體大小在週期性地進行有規律的波動。但有趣的是,由於沒有物件需要移入老年代,應用整體的記憶體堆大小並沒有增長。左下角面板中展示在監控期間所有型別的垃圾回收事件,在本例中一直為 ParallelScavenge。當選中一個事件時,右下角將更加細化的顯示此事件,包括此事件的所有階段以及每個階段的時間消耗。

圖 3. 新生代記憶體使用情況示例圖

圖 3. 新生代記憶體使用情況示例圖

從圖中各種各樣的標籤可以看到包含了豐富的資訊,其中包括引用物件被清理的時間和數量,併發回收器中是否有 Promotion 失敗或者 Evacuation 失敗,垃圾回收器自身演算法的配置,等等資訊。

JFR 程式碼檢視

JFR 程式碼檢視展示了從記錄的資料中獲得的基礎分析資訊。如圖 4 所示,第一個標籤頁顯示了所有的包名,在其他分析工具我們並沒見過類似的功能。可以看出圖中使用 Java.Math 方法的執行佔應用程式所有方法呼叫執行總時長的比重為 41%。傳統的分析視角位於底部,例如熱點方法和被分析程式碼的呼叫如下圖所示。

圖 4. Java Flight Recorder 程式碼檢視

圖 4. Java Flight Recorder 程式碼檢視

與其它分析軟體不同,JFR 提供更多模式的程式碼視覺化。Throwables 標籤頁展示應用中的異常處理,還有展示編譯器工作資訊的標籤頁。

JMC 還提供執行緒檢視,I/O 檢視,系統檢視,所有的這些檢視都是為了更好的分析 JFR 所記錄的真實事件。

JFR 事件概述

JFR 記錄並儲存事件流,JMC 提供不同的檢視來分析這些事件,但是 JFR 事件面板(如圖 5 所示)才是分析事件最有效的途徑。

圖 5. Java Flight Recorder 事件檢視

圖 5. Java Flight Recorder 事件檢視

通過左側面板的過濾器可以過濾顯示的事件,在圖中,只選擇了應用級的事件。需要注意的是,只有在 JFR 記錄儲存事件中指定的事件型別才會出現在這裡,在這裡只能在此基礎上進行過濾。

從圖 5 中可以看出,在 66 秒內,應用程式產生 6 種類型的事件,其中 10612 個 JVM 事件和 1536 個 JDK 庫事件。執行緒 Park 和 Monitor 事件消耗時間長的原因在前文中已有討論,在此不再贅述。應用程式有多個執行緒共消耗 40 秒向套接字內寫資料(Socket Write),這對於一個使用 4 核的應用來說是一個正常值,但仍然可以通過減少向套接字內寫入的資料量來提高效能。

同樣的,應用程式中多個執行緒共消耗 143 秒從套接字讀取資料 (Socket Read)。這看起來並不正常,通過檢視這些事件的處理記錄可以發現,由多個執行緒使用阻塞式 I/O 讀取並不連續的管理請求。這些管理請求的時間間隔通常很長,但這些執行緒卻在 read() 方法內被阻塞,所以導致這些執行緒讀取資料時消耗了過多的時間。

對於 Monitor 事件,

準效能分析器可以提供執行緒被阻塞之後呼叫 CPU 和作業系統程式碼等待解鎖的相關資訊,而 Java 本地分析器能夠提供這些 CPU 和作業系統程式碼執行的記錄,正如 JVM 直接將這些資訊提供給 JFR。

JFR 事件是直接獲得於 JVM 之內,通過這些事件能夠非常詳細的瞭解應用程式內部的執行狀況,這是其他工具所不能比的。事件的型別非常之多,在不同版本的 JDK 之間也有所差異。下面列出一些常用的事件型別,第一行描述可以由其他工具獲得的資訊,第二行描述除了使用 JFR 無法獲得資訊。

  • Classloading

    被載入的類的數量和未被載入的類的數量

    載入類的類載入器(classloader),載入一個類需要的時間

  • Thread statistics

    建立執行緒的數量、銷燬執行緒的數量、執行緒快照(dump)

    阻塞指定執行緒的鎖以及被指定鎖阻塞的執行緒

  • Throwables

    應用程式實用的異常類

    應用程式丟擲的異常和錯誤的數量以及建立異常和錯誤的棧記錄

  • TLAB allocation

    記憶體堆中已分配的數量、TLAB(Thread-Local Allocation Buffers)的大小

    在記憶體對中指定物件的記憶體分配以及為這些物件分配記憶體的棧記錄

  • File and socket I/O

    進行 I/O 消耗的時間

    每一次讀寫呼叫的時間消耗,讀寫操作消耗時間過長的檔案或套接字

  • Monitor blocked

    等待監控器的執行緒

    阻塞某個執行緒的監控器、執行緒被阻塞的時間

  • Code cache

    程式碼快取的大小以及內容

    從程式碼快取中移除的方法、程式碼快取的配置

  • Code compilation

    哪些方法被編譯,OSR 編譯,編譯耗時

    JFR 中沒有額外的資訊,但 JFR 總結了多個原始檔的資訊

  • Garbage collection

    GC 的次數,包括每一個階段的次數、每個代的大小

    JFR 中沒有額外的資訊,但 JFR 總結了來自多個工具的資訊

  • Profiling

    檢測分析和取樣分析

    JFR 並不能獲得分析器獲得的豐富資訊,但 JFR 提供了更高層次的概述

回頁首

開啟 JFR

Java Flight Recorder - JFR 預設被設定為關閉狀態。通過在啟動應用程式的命令中加入-XX:+UnlockCommercialFeatures –XX:+FlightRecorder 引數來開啟 JFR,以及相關的一些功能。但是值得注意的是這個命令只是開啟了 JFR 功能,但並沒有開啟記錄程序各種事件,這就需要我們在開啟了 JFR 功能之後,必須通過使用者介面或者命令列來開啟記錄執行執行緒產生記錄的功能。

通過 JMC 開啟 JFR

最簡單的方式是通過 JMC 來開啟 JFR,當 JMC 啟動後,介面上將顯示當前系統中所有的 JVM 程序。JVM 程序以樹形結構展示,展開 JVM 程序節點,通過 Flight Recorder 按鈕開啟 Flight Recorder 視窗,如圖 6 所示。

圖 6. Java Flight Recorder 記錄視窗檢視

圖 6. Java Flight Recorder 記錄視窗檢視

Flight Recorder 有兩種模式,固定時長模式(圖中為 1 分鐘)和持續模式,在持續模式中,使用環形快取儲存最近的事件。

在進行前瞻性分析(Proactive Analysis)時,例如在進行負載測試時開始記錄事件,應該使用固定時長模式,這樣可以很好的瞭解 JVM 在負責測試期間的表現。

持續模式適合反應(Reactive)分析,這樣可以使 JVM 儲存最近的事件並匯出記錄響應這些事件的資料。舉例來說,Weblogic 應用服務中可以設定觸發器,在應用伺服器響應了一個反常事件之後匯出相關資料。

通過命令列開啟 JFR

在 JVM 啟動引數中加入-XX:+FlightRecorderOption=引數啟動記錄事件。引數為一個以逗號間隔的鍵值對資料。下面將介紹這些選項:

  • name=name

    指定記錄的名稱

  • defaultrecording=true/false

    是否在初始化時啟動記錄事件,預設為 false,對於反應分析,應設定為 true

  • settings=path

    JFR 配置檔案的路徑

  • delay=time

    開始記錄前的延時

  • duration=time

    記錄持續時間

  • filename=path

    儲存記錄事件的檔案路徑

  • compress=true/false

    是否使用 gzip 壓縮記錄資料,預設為 false

  • maxage=time

    環形快取中儲存記錄的資料的最長時間

  • maxsize=size

    環形快取佔用的最大空間

為了更靈活的使用 JFR,在執行過程中可以使用 jcmd 命令來動態改變這些選項的值。下面將介紹使用 jcmd 來設定 JFR 相關選項的命令。

% jcmd process_id JFR.start [option_list]

option_list 是以逗號分隔的鍵值對,選項與上述選項一致。當使用持續模式記錄事件時,可以使用下面的命令將環形快取中的資料匯出至檔案中:

% jcm process_id JFR.dump [option_list]

選項包括:

  • name=name

    指定匯出資料的事件記錄名

  • recording=n

    JFR 事件記錄號

  • filename=path

    匯出檔案的路徑

一個程序下可能有多個 JFR 事件記錄在執行,可以通過下面的命令檢視當前所有的事件記錄:

% jcmd process_id JFR.check [verbose]

這些不同的事件記錄以名字或者隨機的記錄號進行區分。使用下面的命令可以停止一個程序內正在執行的事件記錄:

% jcmd process_id JFR.stop [option_list]

選項包括:

  • name=name

    需要停止的事件記錄名

  • recording=n

    需要停止的事件記錄號

  • discard=Boolean

    如果為 true,捨棄之前儲存的資料

  • filename=path

    儲存資料的檔案路徑

回頁首

選擇 JFR 事件

JFR 目前支援 77 種事件型別,大多數型別的事件為週期性事件,每隔固定時間將會產生一次,其他型別的事件為觸發型,只有當達到某種設定的閥值後被觸發,也就是說才會被 JFR 發現並監控起來。

收集事件資料自然會影響應用程式效能,影響的大小由開啟記錄的事件的型別和數量有關。預設情況下,並沒有開啟所有事件型別的記錄,將對效能影響較大的 6 種類型事件關閉,以此達到將 JFR 對應用程的影響降到 1%以內。

但有時開啟一些對應用影響較大的事件型別是值得的,例如 TLAB 是預設關閉的事件型別,但是它能夠幫助人們發現某些物件是否在分配記憶體時直接進入老年代(Old Generation)。同樣的,Profiling 事件雖然預設開啟,但是每 20 毫秒觸發一次,這樣很容以導致取樣分析出現錯誤。

JFR 記錄的事件定義在一個模版中,可以通過 settings 選項指定。JFR 執行時有兩個模版:

  1. 預設模版:事件對應用效能的影響在 1%以內
  2. 分析模版:設定大部分基於閥值(Threshhold)的事件為 10 毫秒觸發一次,分析模版對應用效能的影響大約為 2%。

這些模版由 JMC 模版管理器(Template Manager)管理,模版管理器通過 JMC 中相應的按鈕啟動。儲存模版的目錄有兩個:$HOME/.JMC/<release>directory 和$JAVA_HOME/jre/lib/jfr。在模版管理器中,可以選擇一個全域性模版、本地模版或者定義一個新模版。定義一個新模版是時,需要指定可用的事件,指定這些事件預設的狀態為開啟或者關閉,同時可以定義這些事件的閥值。

在圖 7 中,File Read 事件被開啟並且設定閥值為 15 毫秒,即當讀取檔案超過 15 毫秒時,此事件將觸發。同時此事件還被設定為 File Read 事件生成棧記錄,由於這將增加事件對應用效能的影響,所以將此選項設定為可選配置項。

圖 7. Java Flight Recorder 事件模板示例圖

圖 7. Java Flight Recorder 事件模板示例圖

事件模版檔案為 xml 檔案,所以通過讀取 xml 檔案很容易可以判斷某個事件是否開啟。也可以將本地模版檔案拷貝至全域性模版目錄供其他使用者和團隊使用。

Java Flight Recorder(JFR)提供了深入 JVM 內部去看執行時的狀態。這是由於 JFR 其本身就是 Java 的一部分內嵌其中的。如同其他工具,JFR 的使用某種程度上增加了對應用程式效能的開銷。常規的使用 JFR 還是可以幫助我們收集大量的資訊,而且對效能的開銷是較低的。

回頁首

總結

到此為止,本系列文章已經將常用的 Java 效能分析工具進行了介紹,我們都知道一個優秀的效能分析工具是我們在分析效能問題的關鍵,但是不能完全,甚至過分的依賴工具所能展現的表面,我們更應當銘記,沒有最好的工具,只有是否適合的工具,正如我們講過的工具 A 也許適合大多數的應用程式分析,但是它卻不能夠揭示工具 B 所能指出的問題,所以我們應當學會靈活自由的使用這些工具。

對於效能分析人員來說命令列工具也是非常有幫助,所以不要拒絕它所能帶來的強大力量。它往往能在自動化的效能監控,或者效能測試中發揮巨大作用。

希望這些介紹能夠給效能調優人員和開發人員的日常工作帶來幫助。俗話說金無足赤,人無完人,工具亦如此。每一個工具都有其各自的優劣勢,只有充分了解每個工具適合的場景,才能讓這些工具揚長避短,更加充分地發揮其作用。