火焰圖是進行性能分析的工具,可以通過Flame Graph獲取指定程序的火焰圖,目前IDEA也增添了火焰圖功能,叫做CPU Profiler
Flame Graphs
Flame Graph是一個可視化的性能分析軟件,可以快速準確地識別最頻繁調用的代碼,最終生成交互式的SVG圖片,作者是Brendan Gregg,博客鏈接,名字叫做“火焰圖”是因為它最初的功能是用于顯示CPU上的熱點(正在運行的代碼),看起來像火焰
這里主要講述Java語言中通過火焰圖分析CPU耗時

火焰圖中X軸是所有的采樣點的集合,Y軸表示棧的深度stack depth,每個方框代表一個棧幀stack frame或者說是一個函數(shù),方框的寬帶代表其在CPU上的時間比例(基于采樣),越寬表示其運行的時間更長,或者說是調用的次數(shù)更多。從底層最寬的幀往上讀,代表著函數(shù)的調用關系,不同的分叉代表不同的代碼調用路徑。顏色沒有實際意義,只是隨機選取用于棧幀之間相互區(qū)分,左右順序也沒有什么含義

通過火焰圖可以快速識別和量化的CPU使用情況,也可以收集不同時間或者不同版本的火焰圖,通過比較快速量化性能的變化
分析器的選擇
為了生成火焰圖,需要一個可以對函數(shù)棧軌跡進行采樣的分析器,對于Java,可以使用兩種類型的分析器:
- 系統(tǒng)分析器
System profilers:例如Linux perf_events,它可以分析系統(tǒng)代碼路徑,例如libjvm,GC和內核,但不能分析Java方法 - JVM分析器
JVM profilers:例如hprof,Lightweight Java Profiler (LJP),可以顯示Java方法,但不顯示系統(tǒng)代碼路徑

perf,也叫作Performance Counters for Linux (PCL),perf_events或perf tools,是一個Linux性能分析工具,是Linux 內核的一部分,面向事件通過采樣進行性能分析,有兩種采樣方法:硬件,主要利用CPU的PMU(Performance Monitoring Unit)計數(shù)器統(tǒng)計事件次數(shù),比如L1 Cache失效的次數(shù),分支預測失敗的次數(shù);軟件,例如利用tracepoint作為探測點進行采樣
缺失Java函數(shù)信息的原因:
- JVM內部即時編譯器JIT,不會公開傳統(tǒng)的符號表供系統(tǒng)分析器讀取
- JVM默認使用幀指針寄存器(x86-64上的RBP)作為通用寄存器,與傳統(tǒng)的棧不同
解決:
- 使用開源JVMTI代理
perf-map-agent,創(chuàng)建/tmp/perf-<pid>.map文件,內部列出符號地址(十六進制),大小和符號名稱。perf_events默認查找此文件,如果找到,則將其用于符號轉換 - 使用
-XX:+PreserveFramePointer,以便perf可以精準的獲取棧結構,會導致一定的性能損耗
使用
- 安裝
perf
查看系統(tǒng)上是否有perf,如果沒有就按照提示安裝
sudo apt install linux-tools-common
sudo apt install linux-tools-4.15.0-46-generic
- 確保JDK版本Java 8 update 60 build 19及以上,該版本添加了
-XX:+PreserveFramePointer選項的支持 - 安裝
perf-map-agent
sudo bash
apt-get install cmake
export JAVA_HOME=/path-to-your-new-jdk8
cd /destination-for-perf-map-agent # I use /usr/lib/jvm
git clone --depth=1 https://github.com/jvm-profiling-tools/perf-map-agent
cd perf-map-agent
cmake .
make
會產生一個out目錄,內部包含attach-main.jar
- 安裝FlameGraph,包含perl腳本,可以對捕獲的采樣數(shù)據(jù)進行處理,生成火焰圖
git clone --depth=1 https://github.com/brendangregg/FlameGraph
- 具體使用
首先是啟動目標Java程序
然后進行CPU采樣30秒
sudo perf record -F 99 -a -g -- sleep 30
參數(shù)的含義,具體可以查看sudo perf record -h
-F指定頻率即每秒采樣數(shù),這里以99赫茲的頻率
-a對所有進程進行采樣,可以使用-p指定進程
指定Java目標進程的PID,緩存符號表,其中attach-main.jar文件來自perf-map-agent
java -cp attach-main.jar:$JAVA_HOME/lib/tools.jar net.virtualvoid.perf.AttachOnce PID
sudo chown root /tmp/perf-*.map
對采樣數(shù)據(jù)進行處理,生成SVG類型的火焰圖,其中的stackcollapse-perf.pl和flamegraph.pl來自FlameGraph
sudo perf script | stackcollapse-perf.pl | \
flamegraph.pl --color=java --hash > flamegraph.svg
或者使用jmaps腳本,自動的為所有Java進程創(chuàng)建符號文件
sudo perf record -F 99 -a -g -- sleep 30; sudo jmaps
sudo perf script | stackcollapse-perf.pl | \
flamegraph.pl --color=java --hash > flamegraph.svg
CPU Profiler
比上一個方式簡單,IntelliJ IDEA 2018.3版本引用該功能,類似于火焰圖,目前還是測試版本,只支持Linux和Mac
-
Ctrl+Shift+A打開Find Action對話框,輸入Experimental features,勾選idea.profiler.enabled,啟用該功能

- 安裝perf,并進行配置
//直接配置
sudo sh -c 'echo 1 >/proc/sys/kernel/perf_event_paranoid'
sudo sh -c 'echo 0 >/proc/sys/kernel/kptr_restrict'
//永久配置
sudo sh -c 'echo kernel.perf_event_paranoid=1 >> /etc/sysctl.d/99-perf.conf'
sudo sh -c 'echo kernel.kptr_restrict=0 >> /etc/sysctl.d/99-perf.conf'
sudo sh -c 'sysctl --system'
- 啟動執(zhí)行,點擊菜單欄
Run | Run 'xxxxx' with Async Profiler,執(zhí)行完成后可以查看CPU Profiler選項卡

在CPU Profiler工具窗口中,收集的數(shù)據(jù)顯示在三個選項卡上 - 火焰圖,調用樹和方法列表。 火焰圖左側列出了應用程序相關的線程,可以查看每個線程單獨的火焰圖信息
其他工具
- VTune:Intel自家的性能分析工具,可以分析CPU熱點代碼,內存,IO,GPU等等,支持多種語言,可以獲取到Java程序函數(shù)的調用所占時間的信息,不過正版是需要付費的
- jvisualvm:JDK自帶的分析工具,可以在
Sampler界面進行CPU或Memory采樣,獲取函數(shù)的運行時間,或者不同數(shù)據(jù)類型的對象在內存中的占比
他們能夠統(tǒng)計函數(shù)耗時,但是沒有火焰圖直觀
參考文獻:
Java in Flames
CPU Profiler