類是 Java 的基礎。大規模的 Java 應用是由成千上萬個類構成的。當出現性能問題時,如何才能在這一大堆類中迅速定位性能瓶頸呢?更糟糕的是,有些類是由某個同事在上個世紀編寫的,某些類是第三方提供的,沒有人明白這些類給整個應用帶來了怎樣的性能影響。
VPA 中的兩個工具:Profile Analyzer 和 CallTree Analyzer,對 Java 應用程序的性能分析提供了有力的支持。這兩個工具提供了多個視圖,幫助用戶從不同的角度分析性能數據。通過這兩個工具幫助,用戶可以快速地從這成千上萬行的代碼中找到性能最差的方法或者代碼行。
使用 VPA 分析性能問題的過程可以分爲三步:
- 針對不同性能問題,選擇恰當的性能分析工具;
- 採集性能數據;
- 使用 VPA 分析性能數據;
本文將以小程序 bookstore 爲例,介紹如何使用這兩個工具快速定位性能問題。
小程序 bookstore
bookstore 是個基於 AWT 的圖形界面程序。它讀取圖書索引文件到內存中,然後列出文件中包含的所有圖書分類。如果用戶在分類列表中選中某個圖書分類,圖書列表中會列出所有該分類下的圖書。圖 1 是小程序 bookstore 圖形界面的截圖。
圖 1. bookstore 小程序
bookstore 小程序有兩點性能問題:
- 打開比較大的圖書索引文件需要比較長的等待時間。
- 如果用戶選中一個分類,bookstore 小程序會很長時間不響應,並且 100% 地佔用 CPU 資源。
選擇恰當的性能分析工具
Java 應用的性能問題多種多樣,有 IO 方面的,也有處理器資源方面的。針對不同的性能問題,性能分析師需要使用不同的採樣手段和分析工具。VPA 中有兩個工具可以用於分析 Java 應用的性能問題:Profile Analyzer 和 Call Tree Analyzer。它們適用於不同的性能問題。
- Call Tree Analyzer 可以幫助用戶找到耗時最長的方法以及該方法的所有調用堆棧,對所有與速度相關的性能問題都適用。
- Profile Analyzer 則對於那些和處理器資源密切相關的性能問題(比如 CPU 利用率 100%)有比較強的針對性。
Profile Analyzer
Profile Analyzer 被用於分析抽樣數據。抽樣採樣時,系統每隔一定週期,採樣一次,採集當前正在運行的指令的地址、線程號和進程號。根據被抽樣到的概率不同,可以知道哪些進程、線程、指令消耗了最多的處理器資源。除此之外 , 抽樣採樣給應用造成的額外開銷比較小。Profile Analyzer 爲抽樣採樣數據的分析提供了一系列的文字或者圖形的視圖。通過這些視圖,分析師可以將性能瓶頸定位到進程、線程、類、方法甚至具體的代碼。
小程序 booksotre 的第二個性能問題是 CPU 資源相關的性能問題,通過 Profile Analyzer 可以迅速找到佔用處理器資源最多的代碼。
Call Tree Analyzer
Call Tree Analyzer 被用於分析方法調用追蹤數據。方法調用追蹤數據中記錄了方法調用堆棧和方法調用的起始時間。方法調用追蹤數據可以用於分析各種速度相關的性能問題。Call Tree Analyzer 將方法調用追蹤數據以方法調用樹的形式展現出來。通過對方法調用樹的分析,用戶不僅可以找到自身調用時間最長、總調用時間最長的方法,還可以根據這些方法的調用堆棧瞭解到性能瓶頸形成的原因。
小程序 bookstore 的第一個性能問題有可能是 IO 問題,Profile Analyzer 不適用於這樣的問題,可以用 Call Tree Analyzer 來尋找性能瓶頸。
性能採樣
VPA 支持多種不同格式的性能數據。表 1 中列出了 VPA 支持的 VPA 支持的性能數據格式。這裏以小程序"bookstore"爲例,介紹如何通過 Performance Inspector 採集抽樣數據和方法調用樹。
表 1. VPA 支持的性能數據格式
配置環境變量
使用 Performance Inspector 採集性能數據,首先需要告訴採樣工具相關的環境變量。
清單 1. 設置環境變量
1
2
|
>
cd /d c:\ibmperf\bin >
setrunenv |
採集抽樣結果
TPROF 是 Performance Inspector 提供的抽樣採樣工具。TPROF 允許用戶只採集某一段時間的抽樣。使用 TPROF 對 bookstore 小程序採樣時,
-
首先啓動 TPROF,如清單 2,執行"run.tprof"命令
清單 2. 啓動 TPROF
123> cd /d c:\ibmperf\bin
> setrunenv
> run.tprof
-
然後,如清單 3,打開另外一個命令行窗口,執行命令 java -Xjit:enableJVMPILineNumbers -Xrunjprof:tprof,fnm=C:\ibmperf\bin\log,pidx-jar bookstore.jar,啓動 bookstore 小程序。
清單 3. runjprof:tprof
12345> cd /d c:\ibmperf\bin
> setrunenv
> java -Xjit:enableJVMPILineNumbers \
-Xrunjprof:tprof,fnm=C:\ibmperf\bin\log,pidx \
-jar bookstore.jar
- 打開圖書索引文件。
- 選中圖書分類的第一項後,立即在 TPROF 的命令行窗口中敲下回車鍵。TPROF 開始採樣。
- 小程序恢復響應並顯示圖書列表後,在 TPROF 的命令行窗口中敲下回車鍵。TPROF 結束採樣。
- 採樣結束後,執行命令"mergetprof",將代碼行等信息合併到抽樣採樣數據中。
- 在 Performance Inspector 安裝目錄的 bin 文件夾下生成了"tprof_e.out”文件。這個文件就是可以被 Profile Analyzer 讀取的抽樣採樣數據。
採集方法調用樹
JPROF 是 Performance Inspector 中的一個工具,可以通過 JVMTI 或 JVMPI 接口紀錄方法的調用信息。對 bookstore 採集方法信息時,
-
首先,如清單 4 執行,命令
java -Xrunjprof:callflow,raw_cycles,start-jar bookstore
, 啓動 bookstore。清單 4. runjprof:calltree
123> cd /d c:\ibmperf\bin
> setrunenv
> java -Xrunjprof:callflow,raw_cycles,start -jar bookstore.jar
- 然後,打開圖書索引文件。
- 文件讀取結束後,關閉 bookstore 小程序。
- 在當前目錄生成了一個 log-rt.xxx文件。將這個文件重命名爲 log-rt.xxx.jprof,Call Tree Analyzer 就可以打開這個 Call Tree 文件。
分析採樣結果
使用 VPA 打開採樣數據文件,VPA 會根據性能數據的文件類型,自動打開相應的性能分析工具。VPA 提供了大量圖型的和文字的視圖,幫助用戶從不同角度分析性能採樣。
分析抽樣結果
使用 Profile Analyzer 分析 Java 程序的性能問題時,編輯器、Source Code 視圖和 Java Hierarchy 視圖是比較常用的控件。
編輯器可以幫助用戶找到 Ticks 最多的方法。如圖 2,tprof_e.out 打開後,編輯器按照"Process > Module"或者"Process > Thread >Module"等不同分層模式以樹狀結構列出了採樣到的進程、線程和模塊。JITCODE 模塊是 Java 進程中 Ticks 最多的模塊,而 BestAuthor$1.valueChanged 方法是 JITCODE 模塊中 Ticks 最多的方法。
圖 2. Profile Analyzer 的編輯器
Source Code 視圖可以幫助用戶找到 Ticks 最多的代碼行。在編輯器中雙擊方法 BestAuthor$1.valueChanged,VPA 會彈出對話框詢問這個方法相應的源文件。如圖 3,爲方法 BestAuthor$1.valueChanged 指定源文件後,通過 Source Code 視圖,可以找到 Ticks 最多的代碼行。這樣的代碼行很有可能是程序的性能瓶頸。
圖 3. Source Code 視圖
通過"Java Hierarchy"視圖,用戶可以按照 Java 方法所屬的包和類逐層瀏覽這些被採集到的 Java 方法。如圖 4,Java Hierarchy 視圖的右側表格中列出了包 bookstore.ui 下所有被採樣到的方法。
圖 4. Java Hierarchy 視圖
分析方法調用樹
Call Tree Analyzer 提供了編輯器、Method Overview 視圖、Call Stack 視圖等控件。
如圖 5,編輯器以方法調用樹和執行圖的方式來展現 JProf 文件。
圖 5. 方法調用樹和執行圖
"Method Overview"視圖列出了方法調用樹中所有的方法。如圖 6,點擊 RAW_CYCLES 列,"Method Overview"按照方法自身消耗時間長短對方法列表進行排序。方法自身消耗時間很長的方法很有可能是性能瓶頸。
圖 6. Method Overview 視圖
在"Method Overview"視圖中,雙擊 BookReader.startElement 方法,Call Stack 視圖會顯示 BookReader.startElement 方法的調用堆棧。通過圖 7 中的調用堆棧,用戶可以瞭解到 BookReader.startElement 方法是怎樣被調用的。
圖 7. Call Stack 視圖
在"Call Stack"視圖中,雙擊 BookReader.startElement 方法,可以在 Call Tree 中定位到這個方法。如圖 8,用戶通過瀏覽 Call Tree,可以更清楚地瞭解 BookReader.startElement 方法的上下文。