1. 概要

Javaサンプリングプロファイラーは通常、JVMツールインターフェイス(JVMTI)を使用して設計され、セーフポイントでスタックトレースを収集します。 したがって、これらのサンプリングプロファイラーはセーフポイントバイアス問題の影響を受ける可能性があります。

アプリケーションの全体像を把握するには、スレッドがセーフポイントにある必要がなく、セーフポイントバイアスの問題を回避するためにいつでもスタックトレースを収集できるサンプリングプロファイラーが必要です

このチュートリアルでは、 async-profiler と、それが提供するさまざまなプロファイリング手法について説明します。

2. 非同期プロファイラー

async-profiler は、 HotSpotJVMに基づく任意のJDKのサンプリングプロファイラーです。 オーバーヘッドが低く、JVMTIに依存しません。

HotSpotJVMが提供するAsyncGetCallTrace APIを使用してJavaコードパスをプロファイリングし、Linuxの perf_events を使用してネイティブコードパスをプロファイリングすることにより、セーフポイントバイアスの問題を回避します。

つまり、プロファイラーはJavaコードとネイティブコードパスの両方のコールスタックを照合して、正確な結果を生成します。

3. 設定

3.1. インストール

まず、プラットフォームに基づいたasync-profilerの最新リリースをダウンロードします。 現在、LinuxおよびmacOSプラットフォームのみをサポートしています。

ダウンロードしたら、プラットフォームで動作しているかどうかを確認できます。

$ ./profiler.sh --version
Async-profiler 1.7.1 built on May 14 2020
Copyright 2016-2020 Andrei Pangin

async-profilerで利用可能なすべてのオプションを事前に確認することをお勧めします。

$ ./profiler.sh
Usage: ./profiler.sh [action] [options] 
Actions:
  start             start profiling and return immediately
  resume            resume profiling without resetting collected data
  stop              stop profiling
  check             check if the specified profiling event is available
  status            print profiling status
  list              list profiling events supported by the target JVM
  collect           collect profile for the specified period of time
                    and then stop (default action)
Options:
  -e event          profiling event: cpu|alloc|lock|cache-misses etc.
  -d duration       run profiling for  seconds
  -f filename       dump output to 
  -i interval       sampling interval in nanoseconds
  -j jstackdepth    maximum Java stack depth
  -b bufsize        frame buffer size
  -t                profile different threads separately
  -s                simple class names instead of FQN
  -g                print method signatures
  -a                annotate Java method names
  -o fmt            output format: summary|traces|flat|collapsed|svg|tree|jfr
  -I include        output only stack traces containing the specified pattern
  -X exclude        exclude stack traces with the specified pattern
  -v, --version     display version string

  --title string    SVG title
  --width px        SVG width
  --height px       SVG frame height
  --minwidth px     skip frames smaller than px
  --reverse         generate stack-reversed FlameGraph / Call tree

  --all-kernel      only include kernel-mode events
  --all-user        only include user-mode events
  --cstack mode     how to traverse C stack: fp|lbr|no

 is a numeric process ID of the target JVM
      or 'jps' keyword to find running JVM automatically

示されているオプションの多くは、後のセクションで役立ちます。

3.2. カーネル構成

Linuxプラットフォームでasync-profilerを使用する場合は、すべてのユーザーがperf_eventsを使用してコールスタックをキャプチャするようにカーネルを構成する必要があります。

まず、 perf_event_paranoid を1に設定します。これにより、プロファイラーはパフォーマンス情報を収集できます。

$ sudo sh -c 'echo 1 >/proc/sys/kernel/perf_event_paranoid'

次に、 kptr_restrict を0に設定して、カーネルアドレスの公開に関する制限を削除します。

$ sudo sh -c 'echo 0 >/proc/sys/kernel/kptr_restrict'

ただし、async-profilerはmacOSプラットフォームで単独で動作します。

プラットフォームの準備ができたので、プロファイリングアプリケーションを構築し、Javaコマンドを使用して実行できます。

$ java -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints -jar path-to-jar-file

ここでは、正確な結果を得るために強く推奨される-XX:+ UnlockDiagnosticVMOptions -XX:+DebugNonSafepointsJVMフラグを使用してプロファイリングアプリを開始しました

アプリケーションのプロファイリングの準備ができたので、async-profilerでサポートされているさまざまなタイプのプロファイリングについて見ていきましょう。

4. CPUプロファイリング

Async-profiler は、CPUのプロファイリング時に、JVMコード、ネイティブクラス、カーネル関数など、Javaメソッドのサンプルスタックトレースを収集します。

PIDを使用してアプリケーションのプロファイルを作成しましょう。

$ ./profiler.sh -e cpu -d 30 -o summary 66959
Started [cpu] profiling
--- Execution profile --- 
Total samples       : 28

Frame buffer usage  : 0.069%

ここでは、 -e オプションを使用して、cpuプロファイリングイベントを定義しました。 次に、 -d 30秒間サンプルを収集するオプション。

最後に、 -oオプションは、サマリー、HTML、トレース、SVG、ツリーなどの出力形式を定義するのに役立ちます。

アプリケーションのCPUプロファイリング中にHTML出力を作成しましょう。

$ ./profiler.sh -e cpu -d 30 -f cpu_profile.html 66959

ここでは、HTML出力を使用して、サンプルを展開、折りたたみ、および検索できることがわかります。

さらに、 async-profilerは、すぐに使用できるフレームグラフをサポートします。

アプリケーションのCPUプロファイルに.svgファイル拡張子を使用して、フレームグラフを生成してみましょう。

$ ./profiler.sh -e cpu -d 30 -f cpu_profile.svg 66959

ここで、結果のフレームグラフは、Javaコードパスを緑色、C ++を黄色、システムコードパスを赤色で示しています。

5. 割り当てプロファイリング

同様に、バイトコードインストルメンテーションのような煩わしい手法を使用せずに、メモリ割り当てのサンプルを収集できます。

async-profiler は、 TLAB (スレッドローカル割り当てバッファー)ベースのサンプリング手法を使用して、TLABの平均サイズを超えるヒープ割り当てのサンプルを収集します。

alloc イベントを使用することで、プロファイラーがプロファイリングアプリケーションのヒープ割り当てを収集できるようになります。

$ ./profiler.sh -e alloc -d 30 -f alloc_profile.svg 66255

ここでは、オブジェクトのクローン作成によってメモリの大部分が割り当てられていることがわかります。これがなければ、コードを見るときに認識しにくいものです。

6. 壁掛け時計のプロファイリング

また、 async-profiler は、壁掛け時計のプロファイルを使用して、実行中、スリープ中、ブロック中などのステータスに関係なく、すべてのスレッドをサンプリングできます。

これは、アプリケーションの起動時に問題をトラブルシューティングするときに便利です。

wall イベントを定義することにより、すべてのスレッドのサンプルを収集するようにプロファイラーを構成できます。

$ ./profiler.sh -e wall -t -d 30 -f wall_clock_profile.svg 66959

ここでは、 -t オプションを使用して、スレッドごとのモードで壁掛け時計プロファイラーを使用しました。これは、すべてのスレッドをプロファイリングするときに強くお勧めします。

さらに、 list オプションを使用して、JVMでサポートされているすべてのプロファイリングイベントを確認できます。

$ ./profiler.sh list 66959
Basic events:
  cpu
  alloc
  lock
  wall
  itimer
Java method calls:
  ClassName.methodName

7. async-profiler IntelliJIDEAを使用

IntelliJ IDEAは、Javaのプロファイリングツールとしてasync-profilerとの統合を特徴としています。

7.1. プロファイラーの構成

IntelliJIDEAでasync-profilerを構成するには、 [Settings / Preferences]> [Build、Execution、Deployment:]で JavaProfilerメニューオプションを選択します。

また、すばやく使用できるように、IntelliJIDEAが提供するCPUプロファイラーや割り当てプロファイラーなどの事前定義された構成を選択できます。

同様に、プロファイラーテンプレートをコピーして、特定のユースケースに合わせてエージェントオプションを編集できます。

7.2. IntelliJIDEAを使用したプロファイルアプリケーション

プロファイラーを使用してアプリケーションを分析する方法はいくつかあります。

たとえば、アプリケーションを選択して選択できます走るとオプション:

または、ツールバーをクリックして、 走るとオプション:

または、 プロファイラーで実行下のオプション走るメニューをクリックし、<を選択しますプロファイラー構成名>

さらに、実行メニューの下にプロファイラーをプロセスに接続するオプションが表示されます。 添付するプロセスを選択できるダイアログが開きます。

アプリケーションのプロファイリングが完了したら、IDEの下部にあるProfilerツールウィンドウバーを使用してプロファイリング結果を分析できます。

アプリケーションのプロファイリング結果は次のようになります。

フレームグラフ、呼び出しツリー、メソッドリストなどのさまざまな出力形式でスレッドごとの結果が表示されます。

または、[表示]>[ツールウィンドウ]メニューのプロファイラーオプションを選択して、結果を表示することもできます。

8. 結論

この記事では、 async-profiler と、いくつかのプロファイリング手法について説明しました。

最初に、Linuxプラットフォームを使用するときにカーネルを構成する方法と、正確な結果を得るためにアプリケーションのプロファイリングを開始するためのいくつかの推奨されるJVMフラグについて説明しました。

次に、CPU、割り当て、壁掛け時計など、さまざまなタイプのプロファイリング手法を検討しました。

最後に、IntelliJIDEAを使用してasync-profilerでアプリケーションのプロファイリングを行いました。