上周一個偶然的機會聽同事提到了Java FlameGraph,剛實驗了一下,效果非常好。
一、什么是FlameGraph
直接看圖說話。FlameGraph 是 SVG格式,矢量圖,可以隨意擴大縮小,看不清的信息可以放大看。圖中,各種紅橙黃色沒有什么意義,僅僅做區分用;x軸橫條寬度來度量時間指標,表明每個接口實際占用的CPU時間;y軸代表線程棧的層次,從最底下往上表示堆棧的層層調用。通過看圖,可以發現哪個接口占用的CPU時間較多,從而優化;同時,可以發現調用關系。
?
Java火焰圖的作者是Brendan Gregg,他的博客非常有意思,很多關于性能的分析。以下鏈接是對每個類別的火焰圖的詳細說明。
什么是Java Flame Graphs:Java Flame Graphs
On-CPU:CPU Flame Graphs
Off-CPU:Off-CPU Flame Graphs
Memory:Memory Leak (and Growth) Flame Graphs
Hot/Cold:Hot/Cold Flame Graphs
Differential:Differential Flame Graphs
關于火焰圖的PPT(講解得非常詳細):Blazing Performance with Flame Graphs
?
二、如何生成
兩個步驟:1. 需要java profiler生成trace文件? 2. 將trace文件轉換為svg格式的火焰圖文件。
1.?需要java profiler生成trace文件
在使用Profiler對CPU進行采樣時,根據CPU當前執行所處棧位置以及各個函數棧在總的采樣次數所占比例就可以得出各個函數執行時的CPU占用比例。常用的是lightweight-java-profiler。還有其他的選擇,比如honest-profiler,lightweight-java-profiler會從java虛擬機啟動開始采樣,而有時候我們需要在CPU飆高的時候開始,這時候honest-profiler提供的動態啟停功能就有用武之地了。也有使用perf生成火焰圖。(*perf 要研究一下)
下面以lightweight-java-profiler 舉例
(1) 從github下載軟件
(2) 編譯 make all
(3) 生成的程序存放在build-64文件夾下面
(4)(可選)可以更改一些lightweight-java-profiler的一些選項,打開src/globals.h文件。在長時間采樣時,可以適當地減少每秒采樣次數,不然最終生成的文件會很大,分析起來比較麻煩。
// 每秒采樣頻率
static const int kNumInterrupts = 100;
// Maximum number of stack traces線程棧個數
static const int kMaxStackTraces = 3000;
// 采樣棧深度
static const int kMaxFramesToCapture = 128;
kNumInterrupts: 每秒鐘抽取樣本的次數;
kMaxStackTraces: 線程棧的最大數量
kMaxFramesToCapture: 線程棧的深度
?
(5)運行Java程序
java -agentpath:path/to/liblagent.so ......
(6)java程序啟動后會在當前目錄生成一個traces.txt文件,但文件中只有一些說明信息。程序正常結束(不殺掉進程)后,才會寫入具體采樣信息。
?
2.將trace文件轉換為svg格式的火焰圖文件。
(1)從github下載FlameGraph
(2)轉換?
幾個商用的profiler工具都存在上述問題。但是,Oracle Solaris studio利用的是jvmti的一個非標準接口AsyncGetCallTrace來實現,不存在上面問題,Jeremy Manson也利用該接口 實現了一個簡單的profiler工具:lightweight-java-profiler。
?
?
?