メソッドコール記録
メソッドコールを記録することはプロファイラーにとって最も難しいタスクの一つです。なぜなら、結果は正確で完全であり、測定されたデータから導き出される結論が誤らないようにするために、非常に小さなオーバーヘッドで行われる必要があるからです。残念ながら、すべてのアプリケーションタイプに対してこれらすべての要件を満たす単一の測定方法は存在しません。このため、JProfilerではどの方法を使用するかを決定する必要があります。
サンプリング対インストゥルメンテーション
メソッドコールの測定は、「サンプリング」と「インストゥルメンテーション」と呼ばれる2つの基本的に異なる技術で行うことができます。それぞれに利点と欠点があります。サンプリングでは、スレッドの現在の呼び出しスタックを定期的に検査します。インストゥルメンテーションでは、選択されたクラスのバイトコードを修正してメソッドのエントリとエグジットを追跡します。インストゥルメンテーションはすべての呼び出しを測定し、すべてのメソッドの呼び出し回数を生成できます。
サンプリングデータを処理する際、フルサンプリング期間(通常5ms)はサンプリングされた呼び出しスタックに帰属します。多くのサンプルがあると、統計的に正しい図が現れます。サンプリングの利点は、頻度が低いため非常に低いオーバーヘッドを持つことです。バイトコードを修正する必要がなく、サンプリング期間はメソッドコールの典型的な持続時間よりもはるかに長いです。欠点は、メソッドの呼び出し回数を決定できないことです。また、数回しか呼び出されない短時間実行のメソッドはまったく表示されないかもしれません。これはパフォーマンスのボトルネックを探している場合には問題ありませんが、コードの詳細な実行時特性を理解しようとしている場合には不便です。
一方、インストゥルメンテーションは、多くの短時間実行のメソッドがインストゥルメントされると大きなオーバーヘッドを引き起こす可能性があります。このインストゥルメンテーションは、時間測定の固有のオーバーヘッドのために、パフォーマンスホットスポットの相対的重要性を歪めますが、ホットスポットコンパイラによってインライン化されるはずの多くのメソッドが、今や別々のメソッドコールとして残らなければならないためでもあります。長時間かかるメソッドコールの場合、オーバーヘッドは無視できます。主に高レベルの操作を行う良いクラスセットを見つけることができれば、インストゥルメンテーションは非常に低いオーバーヘッドを追加し、サンプリングよりも好ましい場合があります。JProfilerのオーバーヘッドホットスポット検出は、いくつかの実行後に状況を改善することもできます。また、呼び出し回数は、何が起こっているのかを理解するのに非常に重要な情報であることが多いです。
フルサンプリング対非同期サンプリング
JProfilerはサンプリングのために2つの異なる技術的解決策を提供します。「フルサンプリング」は、JVM内のすべてのスレッドを定期的に一時停止し、そのスタックトレースを検査する別のスレッドで行われます。ただし、JVMは特定の「セーフポイント」でのみスレッドを一時停止するため、バイアスが導入されます。高度にマルチスレッド化されたCPUバウンドコードを持っている場合、プロファイルされたホットスポットの分布が歪む可能性があります。一方、コードが重要なI/Oを実行する場合、このバイアスは一般的に問題になりません。
高度にCPUバウンドされたコードの正確な数値を取得するのを助けるために、JProfilerは非同期サンプリングも提供します。非同期サンプリングでは、プロファイリングシグナルハンドラが実行中のスレッド自体で呼び出されます。プロファイリングエージェントはネイティブスタックを検査し、Javaスタックフレームを抽出します。主な利点は、このサンプリング方法ではセーフポイントバイアスがなく、高度にマルチスレッド化されたCPUバウンドアプリケーションのオーバーヘッドが低いことです。ただし、CPUビューでは「Running」スレッド状態のみが観察でき、「Waiting」、「Blocking」または「Net I/O」スレッド状態はこの方法では測定できません。プローブデータは常にバイトコードインストゥルメンテーションで収集されるため、JDBCや類似のデータに対してはすべてのスレッド状態を取得できます。
非同期サンプリングは、呼び出しスタックの終わりだけが利用可能な切り詰められたトレースに悩まされます。このため、非同期サンプリングでは呼び出しツリーはホットスポットビューほど有用ではないことがよくあります。非同期サンプリングはLinuxとmacOSでのみサポートされています。
Java 17以降、JProfilerはHotspot JVMでサンプリングにグローバルセーフポイントを使用せず、ほぼゼロオーバーヘッドでフルサンプリングを実行できます。非同期サンプリングと比較して、単一のスレッドに対してはまだある種のセーフポイントバイアスを導入しますが、JVM内のすべてのスレッドに対するグローバルセーフポイントのオーバーヘッドはもはやありません。非同期サンプリングの欠点を考慮すると、Java 17+ではフルサンプリングを使用することが推奨されます。
メソッドコール記録タイプの選択
プロファイリングに使用するメソッドコール記録タイプは重要な決定であり、すべての状況に対して正しい選択はありませんので、情報に基づいた決定を下す必要があります。新しいセッションを作成するとき、セッション開始ダイアログで使用するメソッドコール記録タイプを尋ねられます。その後いつでも、セッション設定ダイアログでメソッドコール記録タイプを変更できます。
簡単なガイドとして、アプリケーションがスペクトラムの反対側にある2つの明確なカテゴリのいずれかに該当するかどうかをテストする次の質問を考慮してください:
プロファイルされたアプリケーションはI/Oバウンドですか?
これは、多くのWebアプリケーションがRESTサービスやJDBCデータベースコールを待機している場合に当てはまります。その場合、インストゥルメンテーションは、呼び出しツリーフィルタを慎重に選択して自分のコードのみを含める条件下で、最良のオプションとなります。プロファイルされたアプリケーションは高度にマルチスレッド化され、CPUバウンドですか?
例えば、これはコンパイラ、画像処理アプリケーション、または負荷テストを実行しているWebサーバーの場合に当てはまるかもしれません。LinuxまたはmacOSでプロファイリングしている場合、この場合、最も正確なCPU時間を得るために非同期サンプリングを選択する必要があります。
それ以外の場合、「フルサンプリング」が一般的に最も適したオプションであり、新しいセッションのデフォルトとして提案されます。
ネイティブサンプリング
非同期サンプリングはネイティブスタックにアクセスできるため、ネイティブサンプリングも実行できます。デフォルトでは、ネイティブサンプリングは有効になっていません。なぜなら、呼び出しツリーに多くのノードを導入し、ホットスポット計算の焦点をネイティブコードにシフトさせるからです。ネイティブコードにパフォーマンスの問題がある場合、非同期サンプリングを選択し、セッション設定でネイティブサンプリングを有効にすることができます。
JProfilerは、各ネイティブスタックフレームに属するライブラリのパスを解決します。呼び出しツリーのネイティブメソッドノードでは、JProfilerはネイティブライブラリのファイル名を角括弧で始めに表示します。
集約レベルに関しては、ネイティブライブラリはクラスのように機能するため、「クラス」集約レベルでは、同じネイティブライブラリ内のすべての後続の呼び出しが単一のノードに集約されます。「パッケージ」集約レベルは、ネイティブライブラリに関係なく、すべての後続のネイティブメソッドコールを単一のノードに集約します。
選択したネイティブライブラリを排除するには、そのネイティブライブラリからノードを削除し、クラス全体を削除することを選択できます。