【Linux】async-profiler

async-profiler

本文涉及的圖片取自Profiling JVM Applications in Production

async-profiler是一個對系統性能影響很少的Java採樣分析器,不會存在安全點偏差問題. 它具有特定於HotSpot的API,以收集堆棧跟蹤並跟蹤內存分配。探查器可與基於HotSpot JVM的OpenJDK,Oracle JDK和其他Java運行時一起使用。

async-profiler可以跟蹤以下類型的事件:

  • CPU週期
  • 硬件和軟件性能計數器,例如高速緩存未命中,分支未命中,頁面錯誤,上下文切換等。
  • Java堆中的分配
  • 滿足的鎖定嘗試,包括Java對象監視器和ReentrantLocks

一、基本概念

1. JVMTI Agents

某些探查器使用openJDK內部API調用AsyncGetCallTrace(ASGT)便於非安全點收集堆棧跟蹤。AsyncGetCallTrace不是官方的JVM API。要使用ASGT,請先創建一個JVMTI Agents

JVMTI(JVM Tool Interface)是JVM提供的一套標準的C/C++編程接口,是實現Debugger、Profiler、Monitor、Thread Analyser等工具的統一基礎,在主流Java虛擬機中都有實現。

當我們要基於JVMTI實現一個Agent時,需要實現如下入口函數:

// $JAVA_HOME/include/jvmti.h

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved);

使用C/C++實現該函數,並將代碼編譯爲動態連接庫(Linux上是.so),通過-agentpath參數將庫的完整路徑傳遞給Java進程,JVM就會在啓動階段的合適時機執行該函數。在函數內部,我們可以通過JavaVM指針參數拿到JNI和JVMTI的函數指針表,這樣我們就擁有了與JVM進行各種複雜交互的能力。

更多JVMTI相關的細節可以參考官方文檔

2. AsyncGetCallTrace

第一個沒有安全點偏差問題的Java sampling profilers, 用於非安全點收集堆棧跟蹤。生成單個線程的堆棧,而無需等待安全點。

二、分析

1. CPU profiling

在這種模式下,profiler 收集堆棧跟蹤示例,其中包括Java方法、native調用、JVM代碼和內核函數。

爲了能夠準確的生成Java和native代碼的確切性能報告,常用的方法是接收perf_events生成的調用堆棧,並將它們與AsyncGetCallTrace生成的調用堆棧進行匹配。此外Async-profiler還提供了一種可以在AsyncGetCallTrace失敗的某些情況下,恢復堆棧跟蹤的解決方法

2. ALLOCATION profiling

async-profiler不使用侵入性技術,例如字節碼檢測工具或昂貴的DTrace探針,這些技術會對性能產生重大影響。它也不會影響轉義分析或防止JIT優化, 如分配消除。僅測量實際堆分配.

探查器具有TLAB驅動的採樣功能。它依賴於HotSpot特定的回調來接收兩種通知:

  • 在新創建的TLAB中分配對象時(火焰圖中的淺綠色幀)
  • 當在TLAB之外的慢路徑上分配對象時(棕色框架)

這意味着不對每個分配進行計數,而僅對每N kB進行分配,其中N是TLAB的平均大小。這使得堆採樣非常便宜並且適合於生產。另一方面,收集的數據可能不完整,儘管在實踐中通常會反映出最主要的分配來源

採樣間隔可以通過-i選件進行調整。例如,-i 500k平均分配500 KB的空間後,將採樣一個樣本。但是,小於TLAB大小的間隔不會生效。

三、與perf對比

1. perf

  • 僅支持Java 8u60及更高版本(以禁用FPO)
  • 禁用FPO對性能有較小的影響(在極端情況下最高可達10%)
  • 方法名解析需要映射(map)文件:perf-map-agent
  • 不支持interpreter frames
  • 堆棧深度通常限制爲127 - 從Linux 4.8開始可以使用: /proc/sys/kernel/perf_event_max_stack
  • 支持系統範圍內的分析

2. async-profiler

與直接將perf_events與Java代理一起使用相比較,該方式具有以下優點:

  • 它適用於較舊的Java版本,因爲它不需要-XX:+PreserveFramePointer,這個參數只在JDK 8u60和更高版本中可用
  • 不需要引入-XX:+PreserveFramePointer,因爲它可能導致較高的性能開銷,在極少數情況下可能高達10%
  • 不需要生成映射文件來將Java代碼地址映射到方法名
  • 可以與interpreter frames一起工作
  • 不需要爲了後續的進一步分析而需要生成perf.data文件

四、使用教程

1. 安裝

$ git clone https://github.com/jvm-profiling-tools/async-profiler
$ cd async-profiler
$ make

2. 參數

./profiler.sh [action] [options] <pid>

部分參數:

# Actions
start/stop		# 開始分析/結束分析
list			# 顯示可用分析事件的列表。此選項仍然需要PID,因爲受支持的事件可能因JVM版本而異

# Options
-d N 			# 分析持續時間
-e event		# cpu, alloc, lock, cache-misses etc
-f filename     # 將配置文件信息轉儲到的文件名 <filename>

3. 使用

a. 程序運行時動態載入

啓動Java應用程序,然後使用代理開始分析,收集性能情況然後停止分析

$ jps
9234 Jps
8983 Computey
$ ./profiler.sh start 8983
$ ./profiler.sh stop 8983

或者可以通過 -d 指定分析時間

$ ./profiler.sh -d 30 8983

b. 作爲代理啓動

如果您需要在JVM啓動後立即配置一些代碼,而不是使用profiler.sh腳本,則可以在命令行上附加async-profiler作爲代理。例如:

$ java -agentpath:/path/to/libasyncProfiler.so=start,file=profile.svg ...

4. 火焰圖可視化

async-profiler提供了開箱即用的火焰圖支持。指定-o svg參數以將分析結果轉儲爲交互式SVG,可在所有主流瀏覽器中立即查看。另外,如果目標文件名以結尾,則會自動選擇SVG輸出格式.svg

$ jps
9234 Jps
8983 Computey
$ ./profiler.sh -d 30 -f /tmp/flamegraph.svg 8983

五、實驗

本實驗參照該實驗手冊:linux-tracing-workshop

Task 1: Profiling Java Code

首先,運行我們將要分析的Java應用程序。 它是同一主要計數應用程序的Java版本:

$ java slowy/App
Press ENTER to start.

暫時不要按ENTER, 在另一個shell中運行jps找到Slowy應用程序的進程ID

$ jps
6043 App
6076 Jps

然後運行收集工具之後,在Java應用程序的控制檯中按ENTER,以便收集工具記錄一些有意義的工作

# 進入 目錄
./profiler.sh -d 15 6043

默認情況下,分析頻率爲100Hz(每10ms CPU時間)這是打印到Java應用程序終端的輸出示例,顯示Java程序中的瓶頸:slowy.App.isPrime,導致該方法的最熱的調用堆棧來slowy.App.main

Started [cpu] profiling
--- Execution profile ---
Total samples       : 376

Frame buffer usage  : 0.0035%

--- 3729803417 ns (99.20%), 373 samples
  [ 0] slowy.App.isPrime
  [ 1] slowy.App.main

--- 10213575 ns (0.27%), 1 sample
  [ 0] finish_task_switch_[k]
[...]

          ns  percent  samples  top
  ----------  -------  -------  ---
  3729803417   99.20%      373  slowy.App.isPrime
    10213575    0.27%        1  finish_task_switch_[k]
    10013086    0.27%        1  Symbol::operator new(unsigned long, int, Arena*, Thread*)
     9997484    0.27%        1  slowy.App.main

Task 2: Flame Graph visualization

同樣運行Java application, 並查看pid

$ jps
6841 Jps
6827 App

然後可以直接利用該命令,輸出火焰圖:

./profiler.sh -d 15 -f /tmp/flamegraph.svg 6827

ps: async-profiler 支持容器,perf 不支持,待驗證

參考文檔:

1. JVMTI 官方文檔

2. Java應用性能分析工具:async-profiler

3. honest-profiler

4. async-profiler

5. Basics on profiling JVM in Linux

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章