from:http://www.tuicool.com/articles/fMf2quA


FlameGraph

火焰圖 ,簡單通過x軸橫條寬度來度量時間指標,y軸代表線程棧的層次,簡單明了, 容易找出具體的可有化點,非常方便,當然前提是我們通過profiler工具獲取到profiler 數據。

java profiler

java性能調優時,我們經常會用到profiler工具,但是很多時候你可能不知道,大部分的 profiler工具都是有問題的 , ,簡單來說,profiler:增加開銷;修改了你的 代碼,導致java編譯器的優化行為不確定;同時影響了代碼的層次,層次越深自然也影響 執行效率。

當然如果你不是通過上面方式實現,二是通過獲取on-cpu線程的線程棧方式,這又會帶來 一個麻煩的問題:獲取系統范圍的線程棧,jvm必須處于safepoint 狀態,只有當線 程處于safepoint狀態的時候,別的線程才能去獲取它的線程棧,而這個safepoint是由jvm 控制的,這對于profiler非常不利,有可能一個很熱的代碼塊,jvm不會在該代碼塊中間放 置safepoint,導致profiler無法獲得該線程棧,導致錯誤的profiler結果。

上面的問題幾個商用的profiler工具都存在,Oracle Solaris studio利用的是jvmti的一 個非標準接口AsyncGetCallTrace來實現,不存在上面問題,Jeremy Manson也利用該接口 實現了一個簡單的profiler工具: Lightweight Asynchronous Sampling Profiler ,我們 的火焰圖的數據來源就是通過它來獲取的。

lightweight-java-profiler

當然,這個工具只支持hotspot的vm,需要你自己編譯,有些問題需要注意:

  • 如果你需要在rhel上編譯,需要安裝4.6以上版本gcc ,4.4版本不支持。
  • 如果你需要在ubunt上編譯,可能會碰到編譯錯誤 。

編譯的時候,需要主要修改BITS參數,如果你要編譯64Bit,使用命令:

make BITS=64 all

使用方法很簡單,直接在你的啟動命令上添加如下參數:

-agentpath:path/to/liblagent.so[:file=name]

啟動之后,會在啟動目錄下生成trace.txt文件(缺省),該文件就是我們需要的采樣數據。

另外有幾個參數可在編譯時修改,都在global.h文件中。首先是采樣的頻率,缺省是100次 每秒;另外是最大采樣的線程棧,缺省3000,超過3000就忽略(對于復雜的應用明顯不夠) ;最后是棧的深度,缺省是128(對于調用層次深的應用調大)。當然你記錄的東西越多, 也會有性能損耗,我調成30000+256,一刻鐘生成200M文件。

另外特別需要注意,trace不是實時寫入,而是在應用shutdown的時候才寫入的,別kill應 用,否則trace里面什么都沒有。