JAVA效能分析之使用火焰圖

luo_shui發表於2017-02-15

隨著業務的發展,使用介面提供的服務的業務越來越多,不同的業務對呼叫耗時的要求不同,當然,耗時越少越好。而近來已經有三四個呼叫方反映介面呼叫耗時太不穩定,程式碼上的優化已經做了很多,有效果,但還不夠。同時,壓力測試顯示,複雜業務在呼叫量單機3000次/分時CPU就已經100%了,與預期差距太遠。為了改善介面效能,找到效能瓶頸,不得已祭出了大殺器————火焰圖。
網上的關於java火焰圖的講解大部分來自於Brendan Gregg的部落格,火焰圖的效果大致如下圖:
最終生成一個SVG格式的圖片,圖上的每個色塊代表的是一個執行緒棧幀,縱軸方向從下往上表示的是函式呼叫棧,而橫軸方向色塊的寬度表示的是函式呼叫佔用CPU的比例。在使用Profiler對CPU進行取樣時,根據CPU當前執行所處棧位置以及各個函式棧在總的取樣次數所佔比例就可以得出各個函式執行時的CPU佔用比例。

火焰圖生成方式

火焰圖的生成過程有兩步:
1. 使用Profiler工具生成trace檔案;
2. 將trace檔案轉換為svg格式的火焰圖檔案;
Brendan使用的Profiler是lightweight-java-profiler,這個需要自己編譯:
make BITS=64 all
還可以通過更改global.h檔案來配置取樣次數:

// 每秒取樣次數
static const int kNumInterrupts = 100;
// Maximum number of stack traces
static const int kMaxStackTraces = 3000;
// 取樣棧深度
static const int kMaxFramesToCapture = 128;

在長時間取樣時,可以適當地減少每秒取樣次數,不然最終生成的檔案會很大,分析起來比較麻煩。
編譯生成檔案liblagent.so,使用起來很簡單,在java啟動引數新增如下內容:
-agentpath:path/to/liblagent.so
java程式啟動後會在當前目錄生成一個traces.txt檔案,但檔案中只有一些說明資訊。程式正常結束(不是通過kill -9殺掉程式)後,才會寫入具體取樣資訊。
Brendan提供了一個工具集來將各種Profiler生成的檔案轉化為火焰圖svg格式。使用方式如下:

git clone http://github.com/brendangregg/FlameGraph
cd FlameGraph
./stackcollapse-ljp.awk < ../traces.txt | ./flamegraph.pl > ../traces.svg

除了使用lightweight-java-profiler作為Profiler外,還有其他的選擇,比如honest-profiler,lightweight-java-profiler會從java虛擬機器啟動開始取樣,而有時候我們需要在CPU飆高的時候開始,這時候honest-profiler提供的動態啟停功能就有用武之地了。
使用honest-profiler生成火焰圖:
首先,需要先下載或者手動編譯honest-profiler,使用方式如下:

java -agentpath:/path/to/location/liblagent.so=interval=7,logPath=/path/to/output/log.hpl,start=0,host=127.0.0.1,port=12345 <normal-java-commandline>

其中interval是取樣頻率,logPath表示生成的取樣檔案路徑,start可取0或1,表示是否啟動時就開啟取樣,host和port表示監聽控制的IP地址和埠號。
啟動成功後,通過telnet連線到127.0.0.1:12345,即可通過命令控制取樣的開始和結束。支援的命令包括:

  • start,開始取樣
  • stop,停止取樣
  • status,列印當前取樣狀態和取樣檔案目錄
  • get [ParamName],獲取引數值
  • set [ParamName] [ParamValue1] ….[ParamValueN] 設定引數值
    [ParamName]可以是intervalMin, intervalMax, interval, maxFrames 或者 logPath

在生成log.hpl檔案後,還需要通過其他工具才能轉換為log.svg檔案,當時是通過開源工具hprof2flamegraph執行的這個過程。使用步驟如下:

git clone https://github.com/chengxiayan/hprof2flamegraph.git
cd hprof2flamegraph
python stackcollapse_hpl.py --discard-lineno --discard-thread log.hpl>log.hpl.output
./flamegraph.pl log.hpl.output>log.svg

其中python版本需要2.7以上,windows上安裝了ActivePerl以後也可以通過執行

perl ./flamegraph.pl log.hpl.output>log.svg

來完成第二步轉換過程。
關於取樣工具的選取,可以看看文章 Evaluating the Accuracy of Java Profilers,這裡面列舉了xprof,hprof,jprofile和yourkit四種取樣器,並通過幾個壓測場景證明了這幾種取樣器的結果是相互矛盾的。總結的原因有三點:
1. 取樣器取樣點不夠隨機,這幾種取樣器都只有在safe point取樣;
2. 不同的取樣器會注入不同的程式碼,從而影響程式優化過程,同時也影響了safe point的分佈,進一步造成取樣差異;
honest-profiler號稱是避開了通過SUN/Oracle management agent去取樣堆疊,而是使用自己實現的使用UNIX 作業系統訊號和為Oracle Performance Studio 設計的內部API的sampling agent,從而提升了取樣準確率。

相關文章