幾款java性能分析器的使用:html
hprofjava
這是一個基於命令行的調試工具,基於JVMTI實現,可用於cpu使用分析,堆分配統計和競爭監視器分析。此外,它還能夠導出完整的堆信息,全部監視器的狀態和jvm裏的線程信息。linux
hprof能夠這樣啓動:git
java -agentlib:hprof=cpu=samples,depth=100,interval=20,lineno=y,thread=y,file=out.hprof myclassgithub
也能夠這樣:安全
java -Xrunhprof[:options] ToBeProfiledClass
分析器會在進程啓動時運行,一直到程序退出或者進程收到一個SIGOUIT信號。bash
分析器會對程序運行棧進行採樣,統計全部處於running狀態的線程,累計這些活躍的堆棧的軌跡,這樣累計數量最多的就極可能是cpu使用熱點的堆棧。在上面的示例中,採樣的棧深度爲最大100層,每20ms採樣一次,輸出的結果文件爲:out.hprof,結果內容包括了調用棧的列表和每一個棧的累計調用計數。示例以下:oracle
TRACE 301130: (thread=200001) sun.security.ssl.SSLSocketFactoryImpl.createSocket(SSLSocketFactoryImpl.java:72) javax.net.ssl.SSLContextSpi.getDefaultSocket(SSLContextSpi.java:143) javax.net.ssl.SSLContextSpi.engineGetDefaultSSLParameters(SSLContextSpi.java:168) javax.net.ssl.SSLContext.getDefaultSSLParameters(SSLContext.java:419) com.netease.hmail.server.netty.NettyServer.prepare(NettyServer.java:503) com.netease.hmail.server.launcher.HmailLauncher.prepare(HmailLauncher.java:588) com.netease.hmail.server.launcher.HmailLauncher.main(HmailLauncher.java:415) TRACE 301159: (thread=200001) com.netease.hmail.server.launcher.HmailLauncher.prepare(HmailLauncher.java:591) com.netease.hmail.server.launcher.HmailLauncher.main(HmailLauncher.java:415) CPU SAMPLES BEGIN (total = 69) Sat Mar 16 11:17:11 2019 rank self accum count trace method 1 5.80% 5.80% 4 301179 java.net.SocketInputStream.socketRead0 2 4.35% 10.14% 3 300841 java.lang.ClassLoader$NativeLibrary.load 3 2.90% 13.04% 2 300901 java.io.BufferedInputStream.<init> 4 1.45% 14.49% 1 300024 java.lang.System.arraycopy
這款分析器有個缺點致使它不是很靠譜:hprof在採用java的線程運行狀態時,是經過state==JVMTI_THREAD_STATE_RUNNABLE來判斷線程是否在佔用cpu的,可是runnable狀態實際上並不表示線程正在運行,它只表示線程是活躍的,可運行的,它是jvm意義上的runnable,跟操做系統內核調度器的"runnable"並非同一個含義。因此hprof誤覺得這些線程在佔用大量cpu而實際上cpu可能很空閒,因此根據hprof獲得的結果可能不許確,這點要注意。jvm
hprof還有另外一個問題是它只在安全點(safepoings)時才進行採樣,這樣就不是在設置的時間間隔進行採樣了,因此hprof是不太可靠的。socket
perf + perf-map-agent + FlameGraph
perf是linux系統級別的分析調試工具,它能夠分析程序的各個調用鏈對cpu的使用進行採樣,從而分析出哪一個調用消耗的cpu最多。 perf的使用示例以下:
perf record -F 99 -p 13204 -g -- sleep 30 perf report -n --stdio
perf record命令將每秒採樣99次(-F 99),目標進程id爲13204(-p 13204),同時將採集程序調用棧(-g)。採樣的結果能夠解析成火焰圖,這樣看起來會更加直觀,解析火焰圖能夠用FlameGraph,該工具能夠從github上下載:
# git clone https://github.com/brendangregg/FlameGraph # or download it from github # cd FlameGraph # perf record -F 99 -a -g -- sleep 60 # perf script | ./stackcollapse-perf.pl > out.perf-folded # ./flamegraph.pl out.perf-folded > perf-kernel.svg
perf的採樣對於java程序有個問題:它不能識別java堆棧裏的方法路徑名稱,只能以十六進制方式來表示,因此須要一個java符號轉換表與perf進行結合來對java程序進行採樣分析,這就是perf-map-agent所作的事。
perf-map-agent
這是一個JVMTI agent,它會爲perf提供一份java符號表供轉換用,另外,須要在jvm啓動參數中加上: -XX:+PreserveFramePointer ,這樣perf才能準確採樣。
安裝和使用perf-map-agent:
sudo bash apt-get install cmake export JAVA_HOME=/path-to-your-new-jdk8 cd /destination-for-perf-map-agent git clone --depth=1 https://github.com/jvm-profiling-tools/perf-map-agent cd perf-map-agent cmake . make
perf-map-agent若是要使用生成火焰圖的腳本須要依賴FlameGraph:
git clone --depth=1 https://github.com/brendangregg/FlameGraph
編譯後就可使用bin目錄下的工具進行調試分析了,工具說明以下:
create-java-perf-map.sh <pid> <options*>
takes a PID and options. It knows where to find libraries relative to the bin
directory.perf-java-top <pid> <perf-top-options>
takes a PID and additional options to pass to perf top
. Uses the agent to create a new /tmp/perf-<pid>.map
and then calls perf top
with the given options.perf-java-record-stack <pid> <perf-record-options>
takes a PID and additional options to pass to perf record
. Runsperf record -g -p <pid> <perf-record-options>
to collect performance data including stack traces. Afterwards it uses the agent to create a new /tmp/perf-<pid>.map
file.perf-java-report-stack <pid> <perf-record-options>
calls first perf-java-record-stack <pid> <perf-record-options>
and then runs perf report
to directly analyze the captured data. You can call perf report -i /tmp/perf-<pid>.data
again with any options after the script has exited to further analyze the data from the previous run.perf-java-flames <pid> <perf-record-options>
collects data with perf-java-record-stack
and then creates a visualization using @brendangregg's FlameGraph tools. To get meaningful stacktraces spanning several JIT-compiled methods, you need to run your JVM with -XX:+PreserveFramePointer
(which is available starting from JDK8 update 60 build 19) as detailed in ag netflix blog entry.create-links-in <targetdir>
will install symbolic links to the above scripts into <targetdir>
.dtrace-java-record-stack <pid>
takes a PID. Runsdtrace
to collect performance data including stack traces. Afterwards it uses the agent to create a new /tmp/perf-<pid>.map
file.dtrace-java-flames <pid>
collects data with dtrace-java-record-stack
and then creates a visualization using @brendangregg's FlameGraph tools. To get meaningful stacktraces spanning several JIT-compiled methods, you need to run your JVM with -XX:+PreserveFramePointer
(which is available starting from JDK8 update 60 build 19) as detailed in ag netflix blog entry.編譯後會生成 attach-main.jar和libperfmap.so兩個文件,這是獲取java程序運行時符號表的關鍵,而FlameGraph下面的jmaps腳本正是依賴於perf-map-agent來生成火焰圖所須要的java符號表,因此perf-map-agent和FlameGraph能夠說是相互依賴,查看一下二者的腳本就明白了。
下面的步驟能夠生成火焰圖:
git clone --depth=1 https://github.com/brendangregg/FlameGraph sudo bash perf record -F 49 -a -g -- sleep 30; ./FlameGraph/jmaps perf script > out.stacks01 cat out.stacks01 | ./FlameGraph/stackcollapse-perf.pl | grep -v cpu_idle | \ ./FlameGraph/flamegraph.pl --color=java --hash > out.stacks01.svg
也能夠直接使用perf-java-map下面的腳本:
sudo ./bin/perf-java-flames 29685
注意:在使用這些腳時,須要修改一下腳本里所依賴的項目目錄,如perf命令的名稱和路徑,FlameGraph的目錄等。
一樣是基於jvmti來開發的,下載地址以下:
git clone https://github.com/jvm-profiling-tools/async-profiler
須要編譯一下:
cd async-profiler make
編譯後的腳本放在build目錄下,進行分析的腳本是:profile.sh,啓動採集命令:
./profiler.sh start $pid
中止採集:
./profiler.sh stop $pid
中止採集後會打印採集的結果。可是這樣輸出的結果比較簡單,也沒有產生火焰圖,可使用如下命令來執行採集任務並生成火焰圖:
./profiler.sh -d 10 -o collapsed -f /tmp/collapsed.txt pid ./FlameGraph/flamegraph.pl --colors=java /tmp/collapsed.txt > flamegraph.svg
上面的含義是採集任務執行10秒種,並按collapsed格式輸出到文件中,這樣火焰圖工具就能夠經過解析這種格式來生成火焰圖了。
【參考文獻】
http://www.brendangregg.com/blog/2014-06-09/java-cpu-sampling-using-hprof.html
https://docs.oracle.com/javase/7/docs/technotes/samples/hprof.html
http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html