CPUおよびメモリのプロファイリングは、JVM上のアプリケーションの基本的な構成要素であるオブジェクトとメソッドコールを主に対象としています。一部のテクノロジーでは、実行中のアプリケーションからセマンティックデータを抽出してプロファイラーに表示する、より高レベルのアプローチが必要とされます。
この最も典型的な例は、JDBCを使用したデータベースへの呼び出しのプロファイリングです。呼び出しツリーには、JDBC APIがいつ使用され、それらの呼び出しにどれだけの時間がかかったかが表示されます。しかし、呼び出しごとに異なるSQLステートメントが実行される可能性があり、それらの呼び出しのどれがパフォーマンスのボトルネックの原因になっているのかは分かりません。また、JDBC呼び出しは多くの場合アプリケーションのさまざまな場所から発生するため、汎用的な呼び出しツリーでそれらを検索する代わりに、すべてのデータベース呼び出しを表示する単一のビューを持つことが重要です。
この問題を解決するために、JProfilerはJREの重要なサブシステム向けに多数のプローブを提供しています。プローブは特定のクラスにインストルメンテーションを追加してデータを収集し、「Databases」、「HTTP, RPC & AI」、「JVM & Custom Probes」ビューセクション内の専用ビューに表示します。さらに、プローブは呼び出しツリーにデータをアノテーションできるため、汎用的なCPUプロファイリングと高レベルのデータの両方を同時に確認できます。
JProfilerによって直接サポートされていないテクノロジーに関する詳細情報を取得したい場合は、そのテクノロジー用の独自のプローブを作成できます。一部のライブラリ、コンテナ、またはデータベースドライバーには、独自の組み込みプローブが同梱されている場合があり、アプリケーションでそれらが使用されるとJProfilerに表示されるようになります。
プローブイベント
プローブはオーバーヘッドを追加するため、デフォルトでは記録されず、手動または自動で各プローブに対して個別に記録を開始する必要があります。
プローブの機能に応じて、プローブデータはさまざまなビューに表示されます。最も低いレベルにあるのがプローブイベントです。他のビューにはプローブイベントを集計したデータが表示されます。デフォルトでは、プローブが記録されている場合でもプローブイベントは保持されません。個々のイベントが重要になった場合は、プローブイベントビューでそれらを記録できます。ファイルプローブなどの一部のプローブでは、通常高頻度でイベントが生成されるため、これは一般的には推奨されません。「HTTP server」プローブやJDBCプローブなどの他のプローブでは、はるかに低い頻度でイベントが生成される場合があるため、個々のイベントの記録が適していることがあります。
プローブイベントは、メソッドパラメータ、戻り値、インストゥルメントされたオブジェクト、スローされた例外など、さまざまなソースからプローブ文字列をキャプチャします。プローブは、実際のSQL文字列を構築するためにプリペアドステートメントのすべてのセッター呼び出しをインターセプトする必要があるJDBCプローブのように、複数のメソッドコールからデータを収集する場合があります。プローブ文字列は、プローブによって測定される上位レベルのサブシステムに関する基本情報です。さらに、イベントには開始時間、オプションの所要時間、関連するスレッド、スタックトレースが含まれます。
テーブルの最下部には、表示されたイベントの総数を表示し、テーブル内のすべての数値列を合計する特別な行があります。デフォルトの列では、これにはDuration列のみが含まれます。テーブル上部のフィルターセレクターと組み合わせて、選択したイベントのサブセットに対して収集されたデータを分析できます。デフォルトでは、テキストフィルターはすべてのテキストフィールド列で機能しますが、テキストフィールドの前にあるドロップダウンから特定のフィルター列を選択できます。コンテキストメニューからもフィルターオプションが利用可能であり、例えば、選択したイベントよりも所要時間が長いすべてのイベントをフィルタリングすることができます。
他のプローブビューにもプローブイベントをフィルタリングするオプションが用意されています。プローブテレメトリービューでは時間の範囲を選択でき、プローブ呼び出しツリービューでは選択した呼び出しスタックからイベントをフィルタリングでき、プローブホットスポットビューでは選択したバックトレースまたはホットスポットに基づいてプローブイベントフィルターが提供され、制御オブジェクトビューおよびタイムラインビューでは選択した制御オブジェクトのプローブイベントをフィルタリングするアクションが提供されます。
選択したプローブイベントのスタックトレースが下部に表示されます。複数のプローブイベントが選択されている場合、スタックトレースは累積され、呼び出しツリーとして、バックトレース付きのプローブホットスポットとして、またはバックトレース付きのCPUホットスポットとして表示されます。
スタックトレースビューの隣には、イベントの所要時間、およびオプションで記録されたスループットのヒストグラムビューが表示されます。これらのヒストグラムでマウスを使用して所要時間の範囲を選択し、上のテーブルでプローブイベントをフィルタリングできます。
プローブはさまざまな種類のアクティビティを記録し、プローブイベントにイベントタイプを関連付けることができます。例えば、JDBCプローブは、ステートメント、プリペアドステートメント、およびバッチ実行を異なる色のイベントタイプとして表示します。
個々のイベントが記録される際の過剰なメモリ使用を防ぐために、JProfilerはイベントを統合します。イベント上限はプロファイリング設定で設定され、すべてのプローブに適用されます。最新のイベントのみが保持され、古いイベントは破棄されます。この統合は上位レベルのビューには影響しません。
プローブ呼び出しツリーとホットスポット
プローブ記録はCPU記録と密接に連携して動作します。プローブイベントはプローブ呼び出しツリーに集約され、そこではプローブ文字列が「ペイロード」と呼ばれるリーフノードになります。プローブイベントが作成された呼び出しスタックのみがそのツリーに含まれます。メソッドノード上の情報は、記録されたペイロード名を参照します。例えば、特定の呼び出しスタックでSQLステートメントが合計時間9000 msで42回実行された場合、すべての祖先呼び出しツリーノードに42のイベントカウントと9000 msの時間が追加されます。記録されたすべてのペイロードの累積によって、どの呼び出しパスがプローブ固有の時間を最も消費しているかを示す呼び出しツリーが形成されます。プローブツリーの焦点はペイロードであるため、ビューフィルターはデフォルトでペイロードを検索しますが、コンテキストメニューにはクラスをフィルタリングするモードも用意されています。
CPU記録がオフになっている場合、バックトレースには「No CPU data was recorded」ノードのみが含まれます。CPUデータが部分的にしか記録されていない場合は、これらのノードと実際のバックトレースが混在することがあります。サンプリングが有効になっている場合でも、JProfilerはデフォルトでプローブペイロードの正確な呼び出しトレースを記録します。このオーバーヘッドを回避したい場合は、プロファイリング設定でオフにすることができます。プローブ記録には、データ収集を増やしたりオーバーヘッドを減らしたりするために調整できるチューニングオプションが他にもいくつかあります。
ホットスポットはプローブ呼び出しツリーから計算できます。ホットスポットノードは、CPUビューセクションのようにメソッドコールではなくペイロードになります。これは多くの場合、プローブのビューの中で最も即座に役立つビューです。CPU記録がアクティブな場合は、通常のCPUホットスポットビューと同様に、最上位のホットスポットを開いてメソッドのバックトレースを分析できます。バックトレースノードの数値は、最深のノードからホットスポットの直下のノードまで広がる呼び出しスタックに沿って、どれだけの合計所要時間で何件のプローブイベントが測定されたかを示しています。
プローブ呼び出しツリービューとプローブホットスポットビューの両方で、対応するCPUビューと同様に、スレッドまたはスレッドグループ、スレッドステータス、およびメソッドノードの集約レベルを選択できます。データを比較するためにCPUビューから移動してくる場合、プローブビューでのデフォルトのスレッドステータスはCPUビューのような「Runnable」ではなく「All states」であることに留意することが重要です。これは、プローブイベントにはデータベース呼び出し、socket操作、プロセス実行などの外部システムが関与することが多く、現在のJVMが処理に費やした時間だけでなく合計時間を確認することが重要であるためです。
制御オブジェクト
外部リソースへのアクセスを提供する多くのライブラリは、リソースとのやり取りに使用できる接続オブジェクトを提供します。例えば、プロセスを開始する際、java.lang.Processオブジェクトを使用すると出力ストリームから読み取り、入力ストリームに書き込むことができます。JDBCを使用する場合、SQLクエリを実行するにはjava.sql.Connectionオブジェクトが必要です。JProfilerでこの種のオブジェクトに対して使用される一般的な用語は「制御オブジェクト」です。
プローブイベントをその制御オブジェクトとともにグループ化し、そのライフサイクルを表示することで、問題がどこから発生しているかをより深く理解するのに役立ちます。また、制御オブジェクトの作成は多くの場合高コストであるため、アプリケーションが作成しすぎず、適切にクローズしていることを確認したいはずです。この目的のために、制御オブジェクトをサポートするプローブには「Timeline」ビューと「Control objects」ビューがあり、後者はJDBCプローブの「Connections」のように、より具体的に命名されている場合があります。制御オブジェクトが開かれたり閉じられたりすると、プローブはイベントビューに表示される特別なプローブイベントを作成するため、関連するスタックトレースを検査できます。
タイムラインビューでは、各制御オブジェクトがバーとして表示され、そのカラー化によって制御オブジェクトがいつアクティブであったかが示されます。プローブはさまざまなイベントタイプを記録でき、タイムラインはそれに応じて色分けされます。このステータス情報は、統合されていたり利用できなかったりする可能性があるイベントのリストから取得されるのではなく、最後のステータスから100 msごとにサンプリングされます。制御オブジェクトには、識別を可能にする名前があります。例えば、ファイルプローブはファイル名を持つ制御オブジェクトを作成し、JDBCプローブは制御オブジェクトの名前として接続文字列を表示します。
制御オブジェクトビューには、すべての制御オブジェクトが表形式で表示されます。デフォルトでは、開いている制御オブジェクトと閉じている制御オブジェクトの両方が表示されます。上部のコントロールを使用して、開いている、または閉じている制御オブジェクトのみに表示を制限したり、特定の列の内容をフィルタリングしたりできます。制御オブジェクトの基本的なライフサイクルデータに加えて、テーブルには各制御オブジェクトの累積アクティビティのデータ(イベント数や平均イベント所要時間など)が表示されます。
ここではプローブごとに異なる列が表示され、例えばプロセスプローブでは読み取りイベントと書き込みイベント用に別々の列セットが表示されます。この情報は、個別のイベント記録が無効になっている場合でも利用可能です。イベントビューと同様に、最下部の合計行をフィルタリングと組み合わせて使用することで、制御オブジェクトの部分セットに関する累積データを取得できます。
プローブはネストされたテーブルで特定のプロパティを公開できます。これは、メインテーブルでの情報過多を減らし、テーブルの列により多くのスペースを確保するために行われます。ファイルプローブやプロセスプローブなどのようにネストされたテーブルが存在する場合、各行の左側にプロパティ値テーブルをその場で開く展開ハンドルが表示されます。
タイムライン、制御オブジェクトビュー、およびイベントビューはナビゲーションアクションで接続されています。例えば、タイムラインビューで任意の行を右クリックして他の各ビューにジャンプし、選択した制御オブジェクトのデータのみを表示させることができます。これは、制御オブジェクトIDを選択した値でフィルタリングすることによって実現されます。
テレメトリーとトラッカー
プローブによって収集された累積データから、複数のテレメトリーが記録されます。どのプローブでも、1秒あたりのプローブイベント数や、平均所要時間またはI/O操作のスループットのようなプローブイベントの平均指標を利用できます。制御オブジェクトを持つプローブでは、開いている制御オブジェクトの数も標準的なテレメトリーです。各プローブは追加のテレメトリーを追加できます。例えば、JPAプローブはクエリ数とエンティティ操作数の個別のテレメトリーを表示します。
ホットスポットビューと制御オブジェクトビューには、時間の経過とともに追跡することが興味深い累積データが表示されます。これらの特別なテレメトリーはプローブトラッカーで記録されます。トラッキングを設定する最も簡単な方法は、ホットスポットビューまたは制御オブジェクトビューからAdd Selection To Trackerアクションを使用して新しいテレメトリーを追加することです。どちらの場合も、時間または回数のどちらを追跡するかを選択する必要があります。制御オブジェクトを追跡する場合、テレメトリーはすべての異なるプローブイベントタイプの積み上げ面グラフになります。追跡対象のホットスポットの場合、追跡時間は異なるスレッド状態に分割されます。
プローブテレメトリーは、システムテレメトリーやカスタムテレメトリーと比較するために「Telemetries」セクションに追加できます。その場合、テレメトリーの概要のコンテキストメニューアクションを使用してプローブ記録を制御することもできます。
JDBCとJPA
JDBCプローブとJPAプローブは連携して動作します。JPAプローブとともにJDBCプローブが記録されていた場合、JPAプローブのイベントビューで個々のイベントを展開して、関連するJDBCイベントを確認できます。
同様に、ホットスポットビューは、JPA操作によってトリガーされたJDBC呼び出しを含む特別な「JDBC calls」ノードをすべてのホットスポットに追加します。一部のJPA操作は非同期であり、即座には実行されず、セッションがフラッシュされた後の任意の時点で実行されます。パフォーマンスの問題を探す場合、そのフラッシュのスタックトレースは役に立たないため、JProfilerは既存のエンティティが取得された場所や新しいエンティティが永続化された場所のスタックトレースを記憶し、それらをプローブイベントに関連付けます。その場合、ホットスポットのバックトレースは「Deferred operations」というラベルの付いたノード内に含まれ、そうでない場合は「Direct operations」ノードが挿入されます。
MongoDBプローブのような他のプローブは、直接操作と非同期操作の両方をサポートしています。非同期操作は現在のスレッドでは実行されず、同じJVM内の1つまたは複数の他のスレッド、あるいは別のプロセスのいずれか別の場所で実行されます。このようなプローブの場合、ホットスポット内のバックトレースは「Direct operations」および「Async operations」コンテナノードに分類されます。
JDBCプローブの特別な問題として、IDなどのリテラルデータがSQL文字列に含まれていない場合にのみ、良好なホットスポットが得られるという点があります。プリペアドステートメントが使用されている場合は自動的にそうなりますが、通常のステートメントが実行される場合はそうではありません。後者の場合、ほとんどのクエリが1回だけ実行されるホットスポットのリストが生成される可能性が高くなります。救済策として、JProfilerは未準備のステートメント内のリテラルを置換するためのデフォルト以外のオプションをJDBCプローブ設定で提供しています。デバッグ目的では、イベントビューでリテラルを確認したい場合もあります。そのオプションを無効にすると、JProfilerが非常に多くの異なる文字列をキャッシュする必要がなくなるため、メモリオーバーヘッドが削減されます。
一方で、JProfilerはプリペアドステートメントのパラメータを収集し、プレースホルダーのない完全なSQL文字列をイベントビューに表示します。これもデバッグ時には役立ちますが、不要な場合はメモリを節約するためにプローブ設定でオフにすることができます。
JDBC接続リーク
JDBCプローブには「Connection leaks」ビューがあり、データベースプールに返却されていない、開いたままの仮想データベース接続が表示されます。これは、プールされたデータベースソースによって作成された仮想接続にのみ影響します。仮想接続は、閉じられるまで物理接続をブロックします。
リーク候補には「unclosed」接続と「unclosed
collected」接続の2種類があります。どちらのタイプも、データベースプールから渡された接続オブジェクトがヒープ上に残っているにもかかわらず、close()が呼び出されていない仮想接続です。「Unclosed
collected」接続はガベージコレクションされており、確定的な接続リークです。
「Unclosed」接続オブジェクトは依然としてヒープ上に存在します。Open Sinceの期間が長くなるほど、そのような仮想接続がリーク候補である可能性が高くなります。仮想接続は、10秒以上開いたままになっている場合に潜在的なリークと見なされます。ただし、後からclose()が呼び出される可能性もあり、その場合は「Connection
leaks」ビューのエントリが削除されます。
接続リークテーブルには、接続クラスの名前を示すClass Name列が含まれています。これにより、どのタイプのプールが接続を作成したかが分かります。JProfilerは多数のデータベースドライバーと接続プールを明示的にサポートしており、どのクラスが仮想接続および物理接続であるかを認識しています。未知のプールやデータベースドライバーの場合、JProfilerは物理接続を仮想接続と誤認することがあります。物理接続は多くの場合長寿命であるため、「Connection leaks」ビューに表示されてしまいます。この場合、接続オブジェクトのクラス名を確認することで、誤検知であることを識別するのに役立ちます。
デフォルトでは、プローブ記録を開始しても接続リーク分析は有効になっていません。接続リークビューには個別の記録ボタンがあり、その状態はJDBCプローブ設定のRecord open virtual connections for connection leak analysisチェックボックスに対応しています。イベント記録と同様に、ボタンの状態は保持されるため、分析を一度開始すると、次回のプローブ記録セッションでも自動的に開始されます。
呼び出しツリー内のペイロードデータ
CPU呼び出しツリーを確認する際、プローブがどこでペイロードデータを記録したかを確認できると便利です。そのデータは、測定されたCPU時間を解釈するのに役立ちます。そのため、多くのプローブがCPU呼び出しツリーへの相互リンクを追加しています。例えば、クラスローダープローブは、クラスローディングがどこでトリガーされたかを示すことができます。これは呼び出しツリーでは通常表示されず、予期せぬオーバーヘッドをもたらす可能性があります。呼び出しツリービューでは不透明なデータベース呼び出しも、1クリックするだけで対応するプローブで詳細に分析できます。これは呼び出しツリー分析でも機能し、プローブリンクをクリックすると、プローブ呼び出しツリービューのコンテキストで分析が自動的に繰り返されます。
もう1つの可能性は、CPU呼び出しツリーにペイロード情報をインラインで直接表示することです。関連するすべてのプローブには、その目的のために設定内にAnnotate in call treeオプションが用意されています。その場合、プローブ呼び出しツリーへのリンクは利用できなくなります。各プローブには独自のペイロードコンテナノードがあります。同じペイロード名を持つイベントが集約され、呼び出し回数と合計時間が表示されます。ペイロード名は呼び出しスタックごとに統合され、最も古いエントリは「[earlier calls]」ノードに集約されます。呼び出しスタックごとに記録されるペイロード名の最大数は、プロファイリング設定で設定可能です。
呼び出しツリーの分割
一部のプローブは、プローブ文字列を使用して呼び出しツリーにペイロードデータをアノテーションするのではなく、異なるプローブ文字列ごとに呼び出しツリーを分割します。これは、受信リクエストの異なるタイプごとに呼び出しツリーを個別に確認したいサーバータイプのプローブで特に役立ちます。「HTTP server」プローブはURLをインターセプトし、URLのどの部分を呼び出しツリーの分割に使用するかをきめ細かく制御できます。デフォルトでは、パラメータを含まないリクエストURIパスのみを使用します。
より柔軟性を高めるために、分割文字列を決定するスクリプトを定義できます。スクリプトでは、現在のjavax.servlet.http.HttpServletRequestをパラメータとして受け取り、目的の文字列を返します。
さらに、単一の分割レベルに限定されず、ネストされた複数の分割を定義することもできます。例えば、最初にリクエストURIパスで分割し、次にHTTPセッションオブジェクトから抽出されたユーザー名で分割できます。あるいは、リクエストURIで分割する前に、リクエストメソッドごとにリクエストをグループ化することもできます。
ネストされた分割を使用することで、呼び出しツリー内のレベルごとに個別のデータを確認できます。呼び出しツリーを見ている際に特定のレベルが邪魔になり、「HTTP server」プローブ設定からそれを削除する必要が生じる場合があります。より便利な方法として、記録されたデータを失うことなく、該当する分割ノードのコンテキストメニューを使用して、呼び出しツリー内の分割レベルをその場で一時的にマージしたりマージ解除したりできます。
呼び出しツリーの分割はかなりのメモリオーバーヘッドを引き起こす可能性があるため、慎重に使用する必要があります。メモリの過負荷を回避するために、JProfilerは分割の最大数を制限します。特定の分割レベルの分割上限に達した場合、上限カウンターをリセットするためのハイパーリンクが付いた特別な「[capped nodes]」分割ノードが追加されます。目的に対してデフォルトの上限が低すぎる場合は、プロファイリング設定で増やすことができます。















































