一、前言
開始之前,你需要准備的環境:
Linux系統機器或者虛擬機一台,里面需要安裝的軟件:git、jdk、perl。
二、簡單介紹
java性能分析火焰圖的所做的事情就是能夠分析出java程序運行期間存在的性能問題,因為某段代碼拖慢整個程序執行是不允許的,因此靠火焰圖的繪制和分析就可以找出類似的“問題代碼段”。
那么這個圖是怎么來的呢?首先跟大多數監控系統一樣,數據采集+前端繪圖,這個圖也是根據某些數據繪制而成的,繪圖工具本篇文章采用FlameGraph,而負責收集這些數據的工具,本篇采用async-profiler,這個工具會在程序運行期間向jvm發送信號采集其運行期數據(簡單來說就是通過該工具可以找出程序中占用CPU資源時間最長的代碼塊,這里async-profiler的實現使用了jvmti,戳這里簡單了解一下),然后生成相應的數據格式文件,而FlameGraph則負責讀取和解析數據文件生成對應的火焰圖(svg文件)。
三、使用&安裝
🔥3.1:環境搭建
確認你的機器已經安裝了git、jdk、perl、c++編譯器,部分可參考:安裝雜記
🔥3.2:clone相關項目
下載下來所需要的兩個項目(這里建議放到data目錄下):
git clone https://github.com/jvm-profiling-tools/async-profiler
git clone https://github.com/brendangregg/FlameGraph
🔥3.3:編譯
下載好以后,需要打開async-profiler文件,輸入make指令進行編譯:
cd async-profiler
make
🔥3.4:編寫測試程序
編譯完成后,我們來寫一個簡單的java程序:
public class Test {
public static void main(String[] args) throws Exception {
Test test = new Test();
while (true) {
test.func1();
test.func2();
test.func3();
}
}
public void func1() throws Exception { //調用第一個方法,需要100ms
Thread.sleep(100L);
}
public void func2() throws Exception { //調用第二個方法,需要500ms
Thread.sleep(500L);
}
public void func3() throws Exception { //調用第三個方法,需要1500ms
Thread.sleep(1500L);
}
}
非常簡單的一個java類,main方法里所做的事情也很簡單,現在把這個文件搞到data目錄下,javac命令編譯,java命令啟動。
然后找到這個java程序的進程id:
ps -ef | grep java
root 30937 17605 0 19:12 pts/0 00:00:00 java Test
root 30961 23135 0 19:12 pts/1 00:00:00 /bin/grep --color=auto java
可以確認此時Test類運行時的java進程 pid = 30937,當然也可以使用jps命令直接查看java進程,效果是一樣的。
🔥3.5:生成火焰圖數據
ok,上述步驟完成后,現在進入async-profiler那個項目的目錄下,然后輸入如下指令:
./profiler.sh -d 60 -o collapsed -f /tmp/test_01.txt ${pid}
上面的-d表示的是持續時長,后面60代表持續采集時間60s,-o表示的是采集規范,這里用的是collapsed,-f后面的路徑,表示的是數據采集后生成的數據存放的文件路徑(這里放在了/tmp/test_01.txt),${pid}表示的是采集目標進程的pid,也就是上面提到的30937
回車運行,運行期間阻塞,知道約定時間完成。運行完成后,現在去tmp下看看有沒有對應文件:
🔥3.6:生成svg文件
上一步產生的文件里的內容,肉眼是很難看懂的,所以現在FlameGraph的作用就體現出來了,它可以讀取該文件並生成直觀的火焰圖,現在進入該項目目錄下面,執行如下語句:
perl flamegraph.pl --colors=java /tmp/test_01.txt > test_01.svg
因為是perl文件,這里使用perl指令運行該文件,后面--colors表示着色風格,這里是java,后面的是數據文件的路徑,這里是剛剛上面生成的那個文件/tmp/test_01.txt,最后的test_01.svg就是最終生成的火焰圖文件存放的路徑和文件命名,這里是命名為test_01.svg並保存在當前路徑,運行后看到該文件已經存在於當前目錄下:
🔥3.7:展示
現在下載下來該文件,使用瀏覽器打開,效果如下:
果然還是看不懂啊-_-||
后續會更新這東西怎么看和分析,或者說我這篇文章里的java例子可能並不能很好的體現出什么。
續更
續更,公司內部火焰圖已經上線,通過更為復雜的業務場景生成的圖反而看起來更容易理解一些,因為業務代碼的調用也會打印出來,下面貼一下內部某業務系統火焰圖:
這張圖是在某個業務系統運行時,采樣60s生成的火焰圖,通過這樣一張圖可以看出,x軸為調用順序,y軸為棧深,線條顏色無實際意義(並不是越紅性能越差之類的),線條長度代表CPU執行該方法時所花費的時間占比,一般來說需要關注的就是棧頂,且寬度比較大的那個。因為一般處於棧頂的,而且寬度比較大的調用棧,說明其存在性能問題,這樣分析的原因如下:
棧深度與y軸高度成正比,一般造成性能問題的都在調用棧的棧頂位置,因為棧頂位置的性能問題會間接拖慢整個調用棧,比如上圖中每個棧底的線條都很長,這是因為越往上棧越深,對下層的影響就越大,可以簡單抽象成方法調用:A調用B,B調用C,C慢會間接導致B慢,從而導致A慢,當然符合這種情況就適合之前說的看棧頂分析瓶頸的方法,如果A本身就慢呢?通過火焰圖也是可以看出來的,比如棧底的線條寬度很寬,但是建立在該棧底的調用鏈上,線條都很窄,火焰圖呈現“┻”型,那么就可以認定,棧底方法存在性能問題,一般情況下都是從棧頂看起,視情況而定~