プローブ
CPUとメモリのプロファイリングは主にオブジェクトとメソッドコールに関心があり、JVM上のアプリケーションの基本的な構成要素です。いくつかの技術では、実行中のアプリケーションからセマンティックデータを抽出し、プロファイラに表示するより高レベルのアプローチが必要です。
これの最も顕著な例は、JDBCを使用したデータベースへのコールのプロファイリングです。呼び出しツリーは、JDBC APIを使用したときとそのコールがどれくらいの時間を要するかを示します。しかし、異なるSQLステートメントが各コールで実行される可能性があり、どのコールがパフォーマンスのボトルネックの原因であるかはわかりません。また、JDBCコールはアプリケーションの多くの異なる場所から発生することが多く、一般的な呼び出しツリーを検索する代わりに、すべてのデータベースコールを表示する単一のビューを持つことが重要です。
この問題を解決するために、JProfilerはJREの重要なサブシステムのためのいくつかのプローブを提供します。プローブは特定のクラスに計測を追加してデータを収集し、「Databases」および「JEE & Probes」ビューセクションに専用のビューで表示します。さらに、プローブは呼び出しツリーにデータを注釈付けすることができ、一般的なCPUプロファイリングと高レベルのデータを同時に見ることができます。
JProfilerが直接サポートしていない技術についての詳細情報を取得したい場合は、そのための 独自のプローブを書くことができます。いくつかのライブラリ、コンテナ、またはデータベースドライバは、アプリケーションで使用されるとJProfilerに表示される 埋め込みプローブを提供することがあります。
プローブイベント
プローブはオーバーヘッドを追加するため、デフォルトでは記録されませんが、各プローブの 記録を開始する必要があります。手動または自動で行うことができます。
プローブの能力に応じて、プローブデータは複数のビューに表示されます。最も低いレベルではプローブイベントがあります。他のビューはプローブイベントを累積したデータを表示します。デフォルトでは、プローブイベントはプローブが記録されている場合でも保持されません。単一のイベントが重要になる場合、プローブイベントビューでそれらを記録することができます。ファイルプローブのような一部のプローブでは、通常高いレートでイベントを生成するため、これは一般的に推奨されません。HTTPサーバープローブやJDBCプローブのような他のプローブは、はるかに低いレートでイベントを生成する可能性があるため、単一のイベントを記録することが適切な場合があります。
プローブイベントは、メソッドパラメータ、戻り値、計測されたオブジェクト、スローされた例外など、さまざまなソースからプローブ文字列をキャプチャします。プローブは複数のメソッドコールからデータを収集することができます。たとえば、JDBCプローブは、実際のSQL文字列を構築するために準備されたステートメントのすべてのセッターコールをインターセプトする必要があります。プローブ文字列は、プローブによって測定される高レベルのサブシステムに関する基本情報です。さらに、イベントには開始時間、オプションの期間、関連するスレッド、およびスタックトレースが含まれます。
テーブルの下部には、表示されたイベントの総数を示し、テーブル内のすべての数値列を合計する特別な行があります。デフォルトの列では、これはDuration列のみを含みます。テーブルの上のフィルタセレクタと共に、選択されたイベントのサブセットの収集データを分析することができます。デフォルトでは、テキストフィルタはすべてのテキストフィールド列で機能しますが、テキストフィールドの前のドロップダウンから特定のフィルタ列を選択できます。フィルタオプションはコンテキストメニューからも利用可能で、たとえば、選択されたイベントの期間よりも長いすべてのイベントをフィルタリングすることができます。
他のプローブビューもプローブイベントをフィルタリングするオプションを提供します。プローブテレメトリービューでは時間範囲を選択でき、プローブ呼び出しツリービューでは選択された呼び出しスタックからイベントをフィルタリングできます。プローブホットスポットビューは、選択されたバックトレースまたはホットスポットに基づいてプローブイベントフィルタを提供し、制御オブジェクトとタイムラインビューは選択された制御オブジェクトのプローブイベントをフィルタリングするアクションを提供します。
選択されたプローブイベントのスタックトレースは下部に表示されます。複数のプローブイベントが選択されている場合、スタックトレースは累積され、呼び出しツリー、バックトレース付きプローブホットスポット、またはバックトレース付きCPUホットスポットとして表示されます。
スタックトレースビューの横には、イベント期間のヒストグラムビューと、オプションで記録されたスループットが表示されます。これらのヒストグラムでマウスを使用して期間範囲を選択し、上のテーブルでプローブイベントをフィルタリングすることができます。
プローブはさまざまな種類のアクティビティを記録し、プローブイベントにイベントタイプを関連付けることができます。たとえば、JDBCプローブは、ステートメント、準備されたステートメント、およびバッチ実行を異なる色のイベントタイプとして表示します。
単一のイベントが記録されるときの過剰なメモリ使用量を防ぐために、JProfilerはイベントを統合します。イベントキャップはプロファイリング設定で設定され、すべてのプローブに適用されます。最新のイベントのみが保持され、古いイベントは破棄されます。この統合は高レベルのビューには影響しません。
プローブ呼び出しツリーとホットスポット
プローブ記録はCPU記録と密接に連携しています。プローブイベントはプローブ呼び出しツリーに集約され、プローブ文字列はリーフノード(ペイロードと呼ばれる)になります。プローブイベントが作成された呼び出しスタックのみがそのツリーに含まれます。メソッドノードの情報は記録されたペイロード名を参照します。たとえば、特定の呼び出しスタックでSQLステートメントが42回実行され、合計時間が9000 msであった場合、これはすべての祖先呼び出しツリーノードに42のイベントカウントと9000 msの時間を追加します。記録されたすべてのペイロードの累積は、プローブ固有の時間を最も消費する呼び出しパスを示す呼び出しツリーを形成します。プローブツリーの焦点はペイロードであるため、ビューのフィルタはデフォルトでペイロードを検索しますが、そのコンテキストメニューにはクラスをフィルタリングするモードも提供されています。
CPU記録がオフになっている場合、バックトレースには「No CPU data was recorded」ノードのみが含まれます。CPUデータが部分的にしか記録されていない場合、これらのノードと実際のバックトレースが混在している可能性があります。サンプリングが有効になっている場合でも、JProfilerはデフォルトでプローブペイロードの正確な呼び出しトレースを記録します。このオーバーヘッドを避けたい場合は、プロファイリング設定でオフにすることができます。プローブ記録のデータ収集を増やしたりオーバーヘッドを減らしたりするために調整できる他のいくつかのチューニングオプションがあります。
ホットスポットはプローブ呼び出しツリーから計算できます。ホットスポットノードは、CPUビューセクションのようにメソッドコールではなく、ペイロードになります。これはプローブの最も即時に役立つビューであることがよくあります。CPU記録がアクティブな場合、トップレベルのホットスポットを開いてメソッドバックトレースを分析することができます。通常のCPUホットスポットビューと同様です。バックトレースノードの数字は、ホットスポットの直下のノードから最も深いノードまでの呼び出しスタックに沿って測定されたプローブイベントの数と合計時間を示します。
プローブ呼び出しツリーとプローブホットスポットビューの両方で、スレッドまたはスレッドグループ、スレッドステータス、およびメソッドノードの集約レベルを選択できます。対応するCPUビューと同様です。CPUビューからデータを比較するために来るとき、プローブビューのデフォルトのスレッドステータスが「すべての状態」であり、CPUビューのように「実行可能」ではないことを覚えておくことが重要です。これは、プローブイベントがデータベースコール、ソケット操作、プロセス実行などの外部システムを含むことが多く、現在のJVMがそれに取り組んでいる時間だけでなく、合計時間を見ることが重要だからです。
制御オブジェクト
外部リソースへのアクセスを提供する多くのライブラリは、リソースと対話するために使用できる接続オブジェクトを提供します。たとえば、プロセスを開始すると、java.lang.Process
オブジェクトを使用して出力ストリームから読み取り、入力ストリームに書き込むことができます。JDBCを使用する場合、SQLクエリを実行するにはjava.sql.Connection
オブジェクトが必要です。この種のオブジェクトに対してJProfilerで使用される一般的な用語は「制御オブジェクト」です。
プローブイベントを制御オブジェクトとグループ化し、そのライフサイクルを表示することで、問題の発生源をよりよく理解するのに役立ちます。また、制御オブジェクトの作成はしばしば高価であるため、アプリケーションがそれらを多く作成しすぎず、適切に閉じることを確認したいです。この目的のために、制御オブジェクトをサポートするプローブには、「タイムライン」と「制御オブジェクト」ビューがあり、後者はより具体的に命名されることがあります。たとえば、JDBCプローブでは「Connections」となります。制御オブジェクトが開かれたり閉じられたりすると、プローブはイベントビューに表示される特別なプローブイベントを作成し、関連するスタックトレースを調査することができます。
タイムラインビューでは、各制御オブジェクトがバーとして表示され、その着色は制御オブジェクトがアクティブであったときの状態を示します。プローブは異なるイベントタイプを記録でき、タイムラインはそれに応じて色分けされます。このステータス情報は、イベントのリストから取得されるのではなく、統合されていないか、利用できない場合もありますが、最後のステータスから100 msごとにサンプリングされます。制御オブジェクトには、それらを識別するための名前があります。たとえば、ファイルプローブはファイル名で制御オブジェクトを作成し、JDBCプローブは接続文字列を制御オブジェクトの名前として表示します。
制御オブジェクトビューは、すべての制御オブジェクトを表形式で表示します。デフォルトでは、開いている制御オブジェクトと閉じている制御オブジェクトの両方が表示されます。上部のコントロールを使用して、開いている制御オブジェクトまたは閉じている制御オブジェクトのみを表示するように制限したり、特定の列の内容をフィルタリングしたりできます。制御オブジェクトの基本的なライフサイクルデータに加えて、テーブルは各制御オブジェクトの累積アクティビティに関するデータを表示します。たとえば、イベント数と平均イベント期間です。
プローブによっては、ここに異なる列を表示します。たとえば、プロセスプローブは読み取りイベントと書き込みイベントのための別々の列セットを表示します。この情報は、単一イベント記録が無効になっている場合でも利用可能です。イベントビューと同様に、下部の合計行はフィルタリングと共に使用して、制御オブジェクトの部分セットに関する累積データを取得できます。
プローブは、ネストされたテーブルで特定のプロパティを公開することができます。これは、メインテーブルの情報過多を軽減し、テーブル列により多くのスペースを与えるために行われます。ネストされたテーブルが存在する場合、たとえばファイルおよびプロセスプローブの場合、各行には左側に展開ハンドルがあり、その場でプロパティ値テーブルを開きます。
タイムライン、制御オブジェクトビュー、およびイベントビューは、ナビゲーションアクションで接続されています。たとえば、タイムラインビューでは、行を右クリックして、他のビューのいずれかにジャンプし、選択された制御オブジェクトのデータのみが表示されるようにすることができます。これは、制御オブジェクトIDを選択された値にフィルタリングすることによって実現されます。
テレメトリーとトラッカー
プローブによって収集された累積データから、いくつかのテレメトリーが記録されます。任意のプローブに対して、秒あたりのプローブイベント数と、プローブイベントの平均期間やI/O操作のスループットなどの平均測定値が利用可能です。制御オブジェクトを持つプローブの場合、開いている制御オブジェクトの数も標準的なテレメトリーです。各プローブは追加のテレメトリーを追加することができ、たとえばJPAプローブはクエリ数とエンティティ操作数のための別々のテレメトリーを表示します。
ホットスポットビューと制御オブジェクトビューは、時間の経過とともに追跡するのに興味深い累積データを表示します。これらの特別なテレメトリーはプローブトラッカーで記録されます。トラッキングを設定する最も簡単な方法は、ホットスポットまたは制御オブジェクトビューからAdd Selection to Trackerアクションを使用して新しいテレメトリーを追加することです。どちらの場合も、時間またはカウントを追跡するかどうかを選択する必要があります。制御オブジェクトを追跡する場合、テレメトリーはすべての異なるプローブイベントタイプのための積み重ねられたエリアグラフです。追跡されたホットスポットの場合、追跡された時間は異なるスレッド状態に分割されます。
プローブテレメトリーは、"Telemetries"セクションに追加して、システムテレメトリーやカスタムテレメトリーと比較することができます。その後、テレメトリー概要のコンテキストメニューアクションでプローブ記録を制御することもできます。
JDBCとJPA
JDBCとJPAプローブは連携して動作します。JPAプローブのイベントビューでは、JPAプローブと共にJDBCプローブが記録されている場合、関連するJDBCイベントを表示するために単一のイベントを展開できます。
同様に、ホットスポットビューはすべてのホットスポットに「JDBC calls」ノードを追加し、JPA操作によってトリガーされたJDBCコールを含みます。一部のJPA操作は非同期であり、すぐに実行されず、セッションがフラッシュされたときに任意の後の時点で実行されます。パフォーマンスの問題を探すとき、そのフラッシュのスタックトレースは役に立たないので、JProfilerは既存のエンティティが取得された場所や新しいエンティティが永続化された場所のスタックトレースを記憶し、それらをプローブイベントに結びつけます。その場合、ホットスポットのバックトレースは「Deferred operations」とラベル付けされたノード内に含まれ、それ以外の場合は「Direct operations」ノードが挿入されます。
MongoDBプローブのような他のプローブは、直接操作と非同期操作の両方をサポートしています。非同期操作は現在のスレッドで実行されず、別の場所で、同じJVMの1つまたは複数の他のスレッドで、または別のプロセスで実行されます。そのようなプローブでは、ホットスポットのバックトレースは「Direct operations」と「Async operation」コンテナノードに分類されます。
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呼び出しツリーにクロスリンクを追加します。たとえば、クラスローダープローブは、クラスのロードがトリガーされた場所を示すことができます。これは、呼び出しツリーでは見えず、予期しないオーバーヘッドを追加する可能性があります。呼び出しツリービューで不透明なデータベースコールは、対応するプローブでワンクリックでさらに分析できます。これにより、プローブリンクをクリックすると、プローブ呼び出しツリービューのコンテキストで分析が自動的に繰り返される呼び出しツリー分析でも機能します。
もう一つの可能性は、CPU呼び出しツリーにペイロード情報を直接インラインで表示することです。すべての関連するプローブには、その目的のためにAnnotate in call treeオプションがあります。その場合、プローブ呼び出しツリーへのリンクは利用できません。各プローブには独自のペイロードコンテナノードがあります。同じペイロード名を持つイベントは集約され、呼び出し回数と合計時間が表示されます。ペイロード名は、呼び出しスタックごとに統合され、最も古いエントリは「[earlier calls]」ノードに集約されます。呼び出しスタックごとに記録されるペイロード名の最大数は、プロファイリング設定で設定可能です。
呼び出しツリーの分割
一部のプローブは、呼び出しツリーにペイロードデータを注釈付けするためにプローブ文字列を使用しません。むしろ、各異なるプローブ文字列のために呼び出しツリーを分割します。これは、サーバータイプのプローブに特に有用であり、異なるタイプの受信リクエストごとに呼び出しツリーを個別に表示したい場合に役立ちます。「HTTPサーバー」プローブはURLをインターセプトし、URLのどの部分を呼び出しツリーの分割に使用するかを細かく制御できます。デフォルトでは、パラメータなしでリクエストURIパスのみを使用します。
より柔軟性を持たせるために、分割文字列を決定するスクリプトを定義することができます。スクリプトでは、現在のjavax.servlet.http.HttpServletRequest
をパラメータとして受け取り、希望する文字列を返します。
さらに、単一の分割レベルに制限されず、複数のネストされた分割を定義することができます。たとえば、最初にリクエストURIパスで分割し、次にHTTPセッションオブジェクトから抽出されたユーザー名で分割することができます。また、リクエストメソッドでグループ化してからリクエストURIで分割することもできます。
ネストされた分割を使用することで、呼び出しツリーの各レベルに対して個別のデータを見ることができます。呼び出しツリーを見ているとき、あるレベルが邪魔になることがあり、「HTTPサーバー」プローブ設定からそれを排除する必要があるかもしれません。より便利に、記録されたデータを失うことなく、対応する分割ノードのコンテキストメニューを使用して、呼び出しツリー内で分割レベルを一時的にマージおよびアンマージすることができます。
呼び出しツリーを分割すると、かなりのメモリオーバーヘッドが発生する可能性があるため、慎重に使用する必要があります。メモリオーバーロードを避けるために、JProfilerは分割の最大数を制限します。特定の分割レベルの分割キャップが達成された場合、特別な「[capped nodes]」分割ノードが追加され、キャップカウンタをリセットするためのハイパーリンクが表示されます。デフォルトのキャップが目的に対して低すぎる場合は、プロファイリング設定でそれを増やすことができます。