一款性能調(diào)優(yōu)利器?—?火焰圖
時間:2021-11-08 15:42:00
手機(jī)看文章
掃描二維碼
隨時隨地手機(jī)看文章
[導(dǎo)讀]來源:https://zhenbianshu.github.io/2019/04/application_debug_tools_flamegraph.html|前言工具的進(jìn)化一直是人類生產(chǎn)力進(jìn)步的標(biāo)志,合理使用工具能大大提高我們的工作效率,遇到問題時,合理使用工具更能加快問題排...
| 前言
工具的進(jìn)化一直是人類生產(chǎn)力進(jìn)步的標(biāo)志,合理使用工具能大大提高我們的工作效率,遇到問題時,合理使用工具更能加快問題排查的進(jìn)度。這也是我為什么非常喜歡 shell 的原因,它豐富的命令行工具集加管道特性處理起文本數(shù)據(jù)集來真的精準(zhǔn)而優(yōu)雅,讓人迷醉。
| 介紹
引子
在排查性能問題時,我們通常會把線程棧 dump 出來,然后使用grep --no-group-separator -A 1 java.lang.Thread.State jstack.log | awk 'NR%2==0' | sort | uniq -c | sort -nr類似的 shell 語句,查看大多數(shù)線程棧都在干什么。而由線程棧的出現(xiàn)頻率,來推斷 JVM 內(nèi)耗時最多的調(diào)用。
1587 at sun.misc.Unsafe.park(Native Method)
795 at java.security.Provider.getService(Provider.java:1035)
293 at java.lang.Object.wait(Native Method)
292 at java.lang.Thread.sleep(Native Method)
73 at org.apache.logging.log4j.core.layout.TextEncoderHelper.copyDataToDestination(TextEncoderHelper.java:61)
71 at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
70 at java.lang.Class.forName0(Native Method)
54 at org.apache.logging.log4j.core.appender.rolling.RollingFileManager.checkRollover(RollingFileManager.java:217)
但是這樣有些問題,首先寫 shell 挺費事的,另外如果我想查看自棧頂?shù)诙€棧的最多調(diào)用,即使修改了 shell 命令,結(jié)果也不直觀。
介紹
火焰圖,因其形似火焰而得名,其開源代碼地址:
https://github.com/brendangregg/FlameGraph它是一種 svg 可交互式圖形,我們通過點擊和鼠標(biāo)指向可以展示出更多的信息。下圖就是一個典型的火焰圖,從結(jié)構(gòu)上,它是由多個大小和顏色各異的方塊構(gòu)成,每個方塊上都有字符,它們底部連接在一塊,組成火焰的基底,頂部分出許多”小火苗”。

特性
介紹火焰圖的分析前,我們要首先說明它的特性:
- 由底部到頂部可以追溯一個唯一的調(diào)用鏈,下面的方塊是上面方塊的父調(diào)用。
- 同一父調(diào)用的方塊從左到右以字母序排列。
- 方塊上的字符表示一個調(diào)用名稱,括號內(nèi)是火焰圖指向的調(diào)用在火焰圖中出現(xiàn)的次數(shù)和這個方塊占最底層方塊的寬度百分比。
- 方塊的顏色沒有實際意義,相鄰方塊的顏色差只為了便于查看。
分析
那么,給我們一張火焰圖,我們怎么能看出系統(tǒng)哪里有問題呢?
應(yīng)用場景
每種工具都有其適合的應(yīng)用場景,火焰圖則適合用在:
- 代碼循環(huán)分析:如果代碼中有很大的循環(huán)或死循環(huán)代碼,那么從火焰圖的頂部或接近項部的地方會有很明顯的”平頂”,表示代碼頻繁地在某個線程棧上下切換。但需要注意的是,如果循環(huán)的總耗時不長,在火焰圖上不會很明顯。
- IO 瓶頸/鎖分析:在我們的應(yīng)用代碼中,我們的調(diào)用普遍都是同步的,也就是說在進(jìn)行網(wǎng)絡(luò)調(diào)用、文件 I/O 操作或未成功獲得鎖時,線程會停留在某個調(diào)用上等待 I/O 響應(yīng)或鎖,如果這個等待非常耗時,會導(dǎo)致線程在某個調(diào)用上一直 hang 住,這在火焰圖上表現(xiàn)得會非常清晰。與此相對的是,我們應(yīng)用線程構(gòu)成的火焰圖無法準(zhǔn)確地表達(dá) CPU 的消耗,因為應(yīng)用線程內(nèi)沒有系統(tǒng)的調(diào)用棧,在應(yīng)用線程棧 hang 住時,CPU 可能去做其他事了,導(dǎo)致我們看到耗時很長,而 CPU 卻很閑。
- 火焰圖倒置分析全局代碼:火焰圖倒置有時也會很實用,如果我們的代碼 N 個不同的分支都調(diào)用某一方法,倒置后,所有棧頂相同的調(diào)用被合并在一塊,我們就能看出這個方法的總耗時,也就很容易評估出優(yōu)化這個方法的收益。
| 實現(xiàn)
既然火焰圖這么強(qiáng)大,那么我們該怎么實現(xiàn)呢?
生成工具
brendan gregg 大神已經(jīng)把生成火焰圖的方法用 perl 實現(xiàn)了,開源代碼就在上文的 Github 倉庫中,根目錄下的flamegraph.pl文件就是可執(zhí)行的 perl 文件了。
a;d 3
b;c 3
z;d 5
a;c;e 3
前面是調(diào)用鏈,每個調(diào)用之間用;隔開,每行后面的數(shù)字是調(diào)用棧出現(xiàn)的次數(shù)。

數(shù)據(jù)準(zhǔn)備
至于我們的 jstack 信息如何被處理成上面的格式,大神則為常見的 dump 格式都提供了工具,像stackcollapse-perf.pl可以處理perf命令的輸出,stackcollapse-jstack.pl處理jstack輸出,stackcollapse-gdb.pl處理 gdb 輸出的棧等。
| 小結(jié)
火焰圖總結(jié)完了,以后再遇到性能問題又多了一種應(yīng)對方式。