FlameGraph
火焰圖 ,簡單通過x軸橫條寬度來度量時(shí)間指標(biāo),y軸代表線程棧的層次,簡單明了, 容易找出具體的可有化點(diǎn),非常方便,當(dāng)然前提是我們通過profiler工具獲取到profiler 數(shù)據(jù)。
java profiler
java性能調(diào)優(yōu)時(shí),我們經(jīng)常會用到profiler工具,但是很多時(shí)候你可能不知道,大部分的 profiler工具都是有問題的 , ,簡單來說,profiler:增加開銷;修改了你的 代碼,導(dǎo)致java編譯器的優(yōu)化行為不確定;同時(shí)影響了代碼的層次,層次越深自然也影響 執(zhí)行效率。
當(dāng)然如果你不是通過上面方式實(shí)現(xiàn),二是通過獲取on-cpu線程的線程棧方式,這又會帶來 一個(gè)麻煩的問題:獲取系統(tǒng)范圍的線程棧,jvm必須處于safepoint 狀態(tài),只有當(dāng)線 程處于safepoint狀態(tài)的時(shí)候,別的線程才能去獲取它的線程棧,而這個(gè)safepoint是由jvm 控制的,這對于profiler非常不利,有可能一個(gè)很熱的代碼塊,jvm不會在該代碼塊中間放 置safepoint,導(dǎo)致profiler無法獲得該線程棧,導(dǎo)致錯(cuò)誤的profiler結(jié)果。
上面的問題幾個(gè)商用的profiler工具都存在,Oracle Solaris studio利用的是jvmti的一 個(gè)非標(biāo)準(zhǔn)接口AsyncGetCallTrace來實(shí)現(xiàn),不存在上面問題,Jeremy Manson也利用該接口 實(shí)現(xiàn)了一個(gè)簡單的profiler工具: Lightweight Asynchronous Sampling Profiler ,我們 的火焰圖的數(shù)據(jù)來源就是通過它來獲取的。
lightweight-java-profiler
當(dāng)然,這個(gè)工具只支持hotspot的vm,需要你自己編譯,有些問題需要注意:
- 如果你需要在rhel上編譯,需要安裝4.6以上版本gcc ,4.4版本不支持。
- 如果你需要在ubunt上編譯,可能會碰到編譯錯(cuò)誤 。
編譯的時(shí)候,需要主要修改BITS參數(shù),如果你要編譯64Bit,使用命令:
make BITS=64 all
使用方法很簡單,直接在你的啟動命令上添加如下參數(shù):
-agentpath:path/to/liblagent.so[:file=name]
啟動之后,會在啟動目錄下生成trace.txt文件(缺?。?,該文件就是我們需要的采樣數(shù)據(jù)。
另外有幾個(gè)參數(shù)可在編譯時(shí)修改,都在global.h文件中。首先是采樣的頻率,缺省是100次 每秒;另外是最大采樣的線程棧,缺省3000,超過3000就忽略(對于復(fù)雜的應(yīng)用明顯不夠) ;最后是棧的深度,缺省是128(對于調(diào)用層次深的應(yīng)用調(diào)大)。當(dāng)然你記錄的東西越多, 也會有性能損耗,我調(diào)成30000+256,一刻鐘生成200M文件。
另外特別需要注意,trace不是實(shí)時(shí)寫入,而是在應(yīng)用shutdown的時(shí)候才寫入的,別kill應(yīng) 用,否則trace里面什么都沒有。