java profiler

 幾款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 bindirectory.
  • 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>.dataagain 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.jarlibperfmap.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的目錄等。

async-profiler

一樣是基於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

相關文章
相關標籤/搜索