このドキュメントでは、J2SE Development Kit の Java Virtual Machine Profiler Interface (JVMPI) について説明します。このインタフェースは、Sun の Java 仮想マシン* の実装とともに機能するプロファイラを開発する目的で、ツールベンダーのために提供されています。
注: JavaTM Virtual Machine* Profiler Interface (JVMPI) は、J2SE 5.0 では推奨されていません。その代わりに、新しい JavaTM Virtual Machine Tool Interface (JVMTI) を使用する必要があります。JVMPI は、J2SE の次のメジャーリリースで削除される予定です。
コメントは 「Profiling Feedback」までお送りください。注: このインタフェースの VERSION_1 は、Classic VM で実装されます。Java HotSpot Client VM および Java HotSpot Server VM では、Java HotSpot テクノロジを使ってこのインタフェースの VERSION_1_1 (Java 2 SDK 1.2.2 以降) または VERSION_1_2 (Java 2 SDK 1.4.2 以降) が実装されます。Java Hotspot テクノロジに特有の注意事項がある場合は、その旨記載されます。
[D]プロファイラフロントエンドは、必ずしもプロファイラエージェントと同じプロセスで実行される必要はありません。プロファイラフロントエンドは、同じマシン上の別のプロセスに置いたり、ネットワーク経由で接続されたリモートマシン上のプロセスに置いたりできます。JVMPI は、標準のワイヤプロトコルを指定しません。ツールベンダーは、個々のプロファイラフロントエンドの必要に応じて、ワイヤプロトコルを設計できます。
JVMPI を基にしたプロファイリングツールを使用することにより、多量のメモリが割り当てられている場所、CPU に負荷のかかるホットスポット、不必要なオブジェクトの保持、モニターの競合など、パフォーマンス全般の分析に役立つ多くの情報を取得できます。
JVMPI では、部分的なプロファイリングもサポートされています。つまり、ユーザは、仮想マシンの動作中の特定期間を指定してアプリケーションのプロファイリングを実行したり、特定の種類のプロファイリング情報を選んで取得したりできます。
現在のバージョンの JVMPI では、1 つの仮想マシンにつき 1 つのエージェントだけをサポートできます。
java -Xrunmyprofiler:heapdump=on,file=log.txt ToBeProfiledClassVM は、Java の次のライブラリディレクトリから myprofiler というプロファイラエージェントライブラリを探します。
PATH 環境変数に指定されたディレクトリを検索する
LD_LIBRARY_PATH に指定されたディレクトリを検索する
VM は、jint JNICALL JVM_OnLoad(JavaVM *jvm, char *options, void *reserved);
JavaVM インスタンスへのポインタを第 1 引数、文字列 「heapdump=on,file=log.txt」 を第 2 引数に指定して、JVM_OnLoad 関数を呼び出します。JVM_OnLoad への第 3 引数は予約されており、null に設定されます。
成功した場合、JVM_OnLoad 関数は JNI_OK を返さなければなりません。何らかの理由で JVM_OnLoad 関数の実行が失敗した場合、その関数は JNI_ERR を返さなければなりません。
JavaVM ポインタに対して GetEnv を呼び出すことによって、関数呼び出しインタフェースを取得できます。たとえば、次のコードは、JDK に実装されたバージョンの JVMPI インタフェースを取得します。
JVMPI_Interface *jvmpi_interface; JNIEXPORT jint JNICALL JVM_OnLoad(JavaVM *jvm, char *options, void *reserved) { int res = (*jvm)->GetEnv(jvm, (void **)&jvmpi_interface, JVMPI_VERSION_1); if (res < 0) { return JNI_ERR; } ... /* use entries in jvmpi_interface */ }JVMPI_Interface構造体は、プロファイラエージェントと VM の間の関数呼び出しインタフェースを定義します。/* interface functions */ typedef struct { jint version; /* JVMPI version */ /* ------interface implemented by the profiler------ */ void (*NotifyEvent)(JVMPI_Event *event); /* ------interface implemented by the JVM------ */ jint (*EnableEvent)(jint event_type, void *arg); jint (*DisableEvent)(jint event_type, void *arg); jint (*RequestEvent)(jint event_type, void *arg); void (*GetCallTrace)(JVMPI_CallTrace *trace, jint depth); void (*ProfilerExit)(jint); JVMPI_RawMonitor (*RawMonitorCreate)(char *lock_name); void (*RawMonitorEnter)(JVMPI_RawMonitor lock_id); void (*RawMonitorExit)(JVMPI_RawMonitor lock_id); void (*RawMonitorWait)(JVMPI_RawMonitor lock_id, jlong ms); void (*RawMonitorNotifyAll)(JVMPI_RawMonitor lock_id); void (*RawMonitorDestroy)(JVMPI_RawMonitor lock_id); jlong (*GetCurrentThreadCpuTime)(void); void (*SuspendThread)(JNIEnv *env); void (*ResumeThread)(JNIEnv *env); jint (*GetThreadStatus)(JNIEnv *env); jboolean (*ThreadHasRun)(JNIEnv *env); jint (*CreateSystemThread)(char *name, jint priority, void (*f)(void *)); void (*SetThreadLocalStorage)(JNIEnv *env_id, void *ptr); void * (*GetThreadLocalStorage)(JNIEnv *env_id); void (*DisableGC)(void); void (*EnableGC)(void); void (*RunGC)(void); jobjectID (*GetThreadObject)(JNIEnv *env); jobjectID (*GetMethodClass)(jmethodID mid); /* JNI handle <-> object ID conversions; VERSION_1_1 and newer */ jobject (*jobjectID2jobject)(jobjectID jid); jobjectID (*jobject2jobjectID)(jobject j); /* VERSION_1_2 and newer: */ void (*SuspendThreadList)(jint reqCount, JNIEnv **reqList, jint *results); void (*ResumeThreadList)(jint reqCount, JNIEnv **reqList, jint *results); } JVMPI_Interface;GetEnv関数はJVMPI_Interfaceへのポインタを返します。JVMPI_Interfaceのversionフィールドは、GetEnvの呼び出しで引き渡すバージョン番号引数と互換性がある、JVMPI のバージョンを示します。ただし、versionフィールドの値は、GetEnvの呼び出しで引き渡すバージョン引数と常に同じであるとは限らないので注意してください。
GetEnvによって返されるJVMPI_Interfaceには、NotifyEvent 以外のすべての関数が設定されています。プロファイラエージェントは、JVM_OnLoad から復帰する前に、NotifyEvent 関数のポインタを設定する必要があります。1.3. イベントの通知
VM は、JVMPI_Event データ構造体を引数として NotifyEvent を呼び出すことにより、イベントを送信します。サポートされるイベントは次のとおりです。
- メソッドに入る、およびメソッドから出る
- オブジェクトの割り当て、移動、および解放
- ヒープ領域の作成および削除
- GC の開始および終了
- JNI グローバル参照の割り当ておよび解放
- JNI グローバル弱参照の割り当ておよび解放
- コンパイルされたメソッドのロードおよびアンロード
- スレッドの開始および終了
- クラスファイルデータの設置準備完了
- クラスのロードおよびアンロード
- 競合する Java モニターに入るのを待つ、入る、および Java モニターから出る
- 競合する raw モニターに入るのを待つ、入る、および raw モニターから出る
- Java モニターを待機する、および待機を終了する
- モニターダンプ
- ヒープダンプ
- オブジェクトダンプ
- プロファイリングデータのダンプ要求またはリセット要求
- Java 仮想マシンの初期化およびシャットダウン
JVMPI_Event 構造体には、イベント型、現在のスレッドの
JNIEnvポインタ、およびその他のイベント特有の情報が格納されます。イベント特有の情報は、イベント特有の構造体の共用体として表現されます。「イベント」の項では、イベント特有の構造体すべてについて説明します。ここでは、クラスロードおよびクラスアンロードのイベントに特有の構造体を示します。typedef struct { jint event_type; /* event_type */ JNIEnv *env_id; /* env where this event occurred */ union { struct { char *class_name; /* class name */ char *source_name; /* name of source file */ jint num_interfaces; /* number of interfaces implemented */ jint num_methods; /* number of methods in the class */ JVMPI_Method *methods; /* methods */ jint num_static_fields; /* number of static fields */ JVMPI_Field *statics; /* static fields */ jint num_instance_fields; /* number of instance fields */ JVMPI_Field *instances; /* instance fields */ jobjectID class_id; /* id of the class object */ } class_load; struct { jobjectID class_id; /* id of the class object */ } class_unload; ... /* Refer to the section on JVMPI events for a full listing */ } u; } JVMPI_Event;1.4. JVMPI の ID
JVMPI では、Java 仮想マシン内のエンティティが、さまざまな種類の ID で参照されます。スレッド、クラス、メソッド、オブジェクト、ヒープ領域、および JNI グローバル参照には、すべて一意の ID があります。
各 ID には、定義イベントおよび定義取り消しイベントがあります。定義イベントは、ID に関連した情報を提供します。 たとえば、スレッド ID の定義イベントには、そのスレッドの名前や、その他のエントリが含まれます。
ID は、定義取り消しイベントが着信するまで有効です。定義取り消しイベントは ID を無効にし、その後その ID の値は別の種類の ID として再利用が可能になります。 たとえば、スレッドの終了後に、そのスレッド ID の値がメソッド ID として再定義されることがあります。
ID データ型 定義イベント 定義取り消しイベント スレッド ID JNIEnv *スレッドの開始 スレッドの終了 オブジェクト ID jobjectIDオブジェクトの割り当て オブジェクトの解放、オブジェクトの移動、および領域の削除 クラス ID jobjectIDクラスのロード クラスのアンロードおよびオブジェクトの移動 メソッド ID jmethodID定義しているクラスのロード 定義しているクラスのアンロード 領域 ID jint領域の新規作成 領域の削除 JNI グローバル参照 ID jobjectグローバル参照の割り当て グローバル参照の解放 定義イベントがプロファイラの初期化中に有効にされている場合、エンティティの作成の通知は、そのエンティティがほかの JVMPI イベントで使われるより前に、定義イベントを介してプロファイラエージェントに届くことが保証されています。
定義イベントが有効にされていない場合、プロファイラエージェントは未知の ID を受け取る可能性があります。 この場合、プロファイラエージェントは、RequestEvent 呼び出しを発行することによって、対応する定義イベントの送信を必要に応じて要求できます。
オブジェクトを表す ID の型は、
jobjectIDです。クラスは、対応するjava.lang.Classオブジェクトのオブジェクト ID によって表されます。このため、クラス ID の型もjobjectIDです。
jobjectIDは、オブジェクトの割り当てイベントによって定義され、そのオブジェクトが割り当てられた領域について次の定義取り消しイベントの 1 つが着信するまでは、その領域内で有効です。
- オブジェクトの解放イベントは、オブジェクト ID を無効にする
- オブジェクトの移動イベントは、特別な種類の定義取り消しイベントである。ほかの定義取り消しイベントは該当するエンティティのライフタイムが終わったことを示すが、このイベントの場合、オブジェクトは依然として存在するが、その ID が変わる。オブジェクトが新しい領域に移動された可能性もある
- 領域の削除イベントは、領域内に残っているすべてのオブジェクト ID を無効にする
オブジェクトの解放イベントまたは領域の削除イベントによってオブジェクト ID が無効にされた場合、そのオブジェクトはガベージコレクトされたと言います。
通常、プロファイラエージェントは、
jobjectIDとオブジェクト ID の内部表現とのマッピングを保持し、JVMPI のオブジェクト ID に対する定義イベントおよび定義取り消しイベントに対応してそのマッピングを更新します。ガベージコレクション (GC) 中にオブジェクト ID が無効にされることがあるので、VM は、
jobjectIDエントリを含むすべてのイベントを、GC を無効にした状態で発行します。また、プロファイリングエージェントは、jobjectIDデータ型を直接操作しているときは、GC を無効にする必要があります。GC を無効にしないと、エージェントコードでjobjectIDが操作されている間に、GC によってそのjobjectIDが無効にされる可能性があります。プロファイラエージェントは、jobjectIDの引数をとる JVMPI 関数、またはjobjectIDの結果を返す JVMPI 関数を呼び出す場合には、必ず GC を無効にする必要があります。ただし、GC がすでに無効にされているイベントハンドラ内で関数を呼び出す場合、プロファイラエージェントは明示的に再度 GC を無効にする必要はありません。スレッドは、
JNIEnvインタフェースポインタか、対応するjava.lang.Threadオブジェクトのオブジェクト ID によって識別が可能です。JNIEnvポインタは、スレッド開始イベントからスレッド終了イベントまで有効で、スレッドが有効な間はずっと一定です。これに対し、java.lang.Threadのオブジェクト ID は、スレッドの終了後も、ガベージコレクトされるまで有効なままです。プロファイラエージェントは、GetThreadObject関数を呼び出すことによって、JNIEnvポインタを、対応するスレッドオブジェクト ID に変換できます。1.5. スレッドとロックの問題
JVMPI は、Java 仮想マシンと同じプロセス内で実行されているプロファイラエージェントによって使用されます。エージェントを記述するプログラマは、データの破壊とデッドロックを防ぐために、スレッドとロックの扱いに注意する必要があります。イベントは、生成されたスレッドと同じスレッド内で送信されます。たとえば、クラスロードイベントは、クラスがロードされるスレッドと同じスレッド内で送信されます。複数のイベントが異なるスレッド内で同時に着信する可能性があります。このため、複数のスレッドが同じデータ構造体を同時に更新することによるデータの破壊を防ぐために、エージェントプログラムは、必要な同期を提供しなければなりません。
頻繁に発生する特定のイベント (メソッドに入る、メソッドから出るなど) の同期が、プログラムの実行に過大なオーバーヘッドを課す場合があります。このため、エージェントは、JVMPI がサポートするスレッドローカルな記憶領域を利用して、グローバルロックを獲得せずにプロファイリングデータを記録し、特定の周期でスレッドローカルなデータをグローバルプロファイルにマージすることができます。JVMPI は、ポインタサイズのスレッドローカル記憶領域をエージェントに提供します。以下に、プロファイラエージェントがこの機能を利用する方法を示す簡単な例を紹介します。ここでは、各スレッド内で実行されたメソッド数をカウントするプロファイラエージェントを記述する必要があるとします。このエージェントは、スレッドの開始イベント、メソッドに入るイベント、およびスレッドの終了イベント用のイベントハンドラをインストールします。
/* thread start event handler * sets up the storage for thread-local method invocation counter */ void ThreadStartHandler(JNIEnv *thread_id) { int *p_ctr = (int *)malloc(sizeof(int)); CALL(SetThreadLocalStorage)(thread_id, p_ctr); } /* method enter event handler * increments thread local method invocation counter */ void MethodEntryHandler(jmethodID method_id, JNIEnv *thread_id) { int *p_ctr = (int *)CALL(GetThreadLocalStorage)(thread_id); (*p_ctr)++; } /* thread end handler * prints the number of methods executed */ void ThreadEndHandler(JNIEnv *thread_id) { int *p_ctr = (int *)CALL(GetThreadLocalStorage)(thread_id); fprintf(stdout, "Thread %x executed %d methods\n", thread_id, (*p_ctr)); free(p_ctr); }次の JVMPI 関数は、関数の実行中に、同じスレッド内でイベントの通知を同期的に送信する可能性があります。
RequestEvent関数は、プロファイラエージェントによって明示的に要求された JVMPI イベントを提供します。CreateSystemThread関数は、スレッドオブジェクトの割り当てイベント、およびスレッドの開始イベントを発行します。RunGC関数は、GC 関連のいくつかのイベントを生成します。プロファイリングエージェントが Java 仮想マシンにロードされる際のプロセスは、GC が有効なマルチスレッドモード、GC が無効なマルチスレッドモード、およびスレッド中断モードのいずれかが可能です。発行される JVMPI イベントは、モードによって異なります。特定の JVMPI 関数は、プロセスを、あるモードから別のモードに変更します。
デッドロックを避けるため、プロファイラエージェントは、次のガイドラインに従う必要があります。
- GC が有効なマルチスレッドモードでは、かなりの程度まで自由にエージェントコードでロックを獲得したり JVMPI 関数を呼び出したりできる。もちろん、デッドロックを回避するための通常の規則が適用される。異なるスレッドが、同じロックのセットに異なる順序で入ってはならない
- GC が無効な場合、エージェントプログラムは、新しい Java オブジェクトの作成を必要とする JVMPI 関数や、ガベージコレクトが実行される可能性のある JVMPI 関数を呼び出してはならない。現在のところ、その種の関数には、
CreateSystemThreadやRunGCなどがある。さらに、GC を無効にすることによってスレッド間に暗黙的にロックの依存性が発生することに、プログラマは注意する必要がある。GC が無効になると、現在のスレッドが特定のロックを安全に獲得できない場合がある。たとえば、あるスレッドが GC を無効にしてロックを獲得しようとした場合に、別のスレッドがすでにそのロックを獲得しており、GC をトリガしていると、デッドロックが発生することがある- スレッド中断モードでは、1 つ以上のスレッドが中断されている。この場合、エージェントプログラムは、現在のスレッドがブロックする可能性のある操作を実行してはならない。このような操作には、たとえば、標準 C ライブラリによって提供される
mallocやfprintfなどがある。これらの関数は、一般に C ライブラリ内部でロックを獲得するが、中断されたスレッドの 1 つによってそれらのロックが保持されている可能性がある1.6. プロファイラエージェントとフロントエンドの間のデータ通信
JVMPI は、プロファイラエージェントが仮想マシンと通信するための下位レベルの機構を提供しています。この機構の目的は、フロントエンドの要件に応じてデータを提示する柔軟性をプロファイラエージェントに与え、仮想マシン側で実行される処理を最小限に抑えることです。このため、JVMPI は、プロファイリングエージェントとフロントエンドの間のワイヤプロトコルを指定していません。その代わりに、ツールベンダーが、各フロントエンドの要件に応じて独自のプロファイリングエージェントを設計します。プロファイラエージェントとフロントエンドを異なるマシンに配置できるようにするには、ワイヤプロトコルを設計するときに、次の点について考慮する必要があります。
- ポインタのサイズ (32 ビットか 64 ビットか、など)。JVMPI のすべての ID はポインタ型である (「データ型」を参照)
- バイト順 (リトルエンディアンまたはビッグエンディアン)
- ビット順 (最上位ビットが先頭、または最下位ビットが先頭)
- 文字列のエンコーディング。JVMPI では、Java 仮想マシンの仕様で定められている UTF-8 エンコーディングが使用される
たとえば、Java 2 SDK 1.4.2 以前に付属している HPROF プロファイラエージェントでは、すべての ID のサイズが最初のレコードとして送信され、整数および浮動小数点のデータには標準ネットワークのバイト順が使用されます。
2. インタフェース関数
- CreateSystemThread
- DisableEvent
- DisableGC
- EnableEvent
- EnableGC
- GetCallTrace
- GetCurrentThreadCpuTime
- GetMethodClass
- GetThreadLocalStorage
- GetThreadObject
- GetThreadStatus
- NotifyEvent
- ProfilerExit
- RawMonitorCreate
- RawMonitorDestroy
- RawMonitorEnter
- RawMonitorExit
- RawMonitorNotifyAll
- RawMonitorWait
- RequestEvent
- ResumeThread
- ResumeThreadList
- RunGC
- SetThreadLocalStorage
- SuspendThread
- SuspendThreadList
- ThreadHasRun
- jobjectID2jobject
- jobject2jobjectID
jint (*CreateSystemThread)(char *name, jint priority, void (*f)(void *));
- プロファイラエージェントによって呼び出され、Java 仮想マシン内にデーモンスレッドを作成します。
プロファイラエージェントからこの関数を呼び出すのを、JVM が
JVMPI_EVENT_INIT_DONEを通知したあと、かつ、システムが GC の有効なマルチスレッドモードのときだけにすると安全です。引数:
name- スレッドの名前 priority- スレッドの優先順位。次の値を指定できる
JVMPI_NORMAL_PRIORITYJVMPI_MAXIMUM_PRIORITYJVMPI_MINIMUM_PRIORITYf- スレッドによって実行される関数 戻り値:
JNI_OK- 成功 JNI_ERR- 失敗
jint (*DisableEvent)(jint event_type, void *arg);
- プロファイラエージェントによって呼び出され、特定の型のイベント通知が無効にされます。プロファイラエージェントは、event_type のほかに、特定のイベント型に特有の補足情報を提供する引数を渡すことも可能です。
VM の起動時には、すべてのイベントが無効にされています。イベントは、一度有効にされると、明示的に無効にされるまで有効のままです。
この関数は、event_type が
JVMPI_EVENT_HEAP_DUMP、JVMPI_EVENT_MONITOR_DUMP、またはJVMPI_EVENT_OBJECT_DUMPの場合には、JVMPI_NOT_AVAILABLEを返します。引数:
event_type- イベントの型。 JVMPI_EVENT_CLASS_LOADなどarg- イベントに特有の情報 戻り値:
JVMPI_SUCCESS- 無効にする操作が成功 JVMPI_FAIL- 無効にする操作が失敗 JVMPI_NOT_AVAILABLE- 指定された event_typeを無効にする操作がサポートされていない
void (*DisableGC)(void);
- プロファイラによって呼び出され、
EnabledGCが呼び出されるまでガベージコレクションを無効にします。DisableGCおよびEnableGCの呼び出しは、入れ子にできます。
jint (*EnableEvent)(jint event_type, void *arg);
- プロファイラエージェントによって呼び出され、特定の型のイベント通知を有効にします。プロファイラエージェントは、event_type のほかに、特定のイベント型に特有の補足情報を提供する引数を渡すことも可能です。
VM の起動時には、すべてのイベントが無効にされています。イベントは、一度有効にされると、明示的に無効にされるまで有効のままです。
この関数は、event_type が
JVMPI_EVENT_HEAP_DUMP、JVMPI_EVENT_MONITOR_DUMP、またはJVMPI_EVENT_OBJECT_DUMPの場合には、JVMPI_NOT_AVAILABLEを返します。プロファイラエージェントがこれらのイベントを要求するには、RequestEvent関数を使う必要があります。引数:
event_type- イベントの型。 JVMPI_EVENT_CLASS_LOADなどarg- イベントに特有の引数 戻り値:
JVMPI_SUCCESS- 有効にする操作が成功 JVMPI_FAIL- 有効にする操作が失敗 JVMPI_NOT_AVAILABLE- 指定された event_typeを有効にする操作がサポートされていない
void (*EnableGC)(void);
- ガベージコレクションを有効にします。
DisableGCおよびEnableGCの呼び出しは、入れ子にできます。
void (*GetCallTrace)(JVMPI_CallTrace *trace, jint depth);
- プロファイラによって呼び出され、指定されたスレッドについて、現在のメソッド呼び出しスタックトレースを取得します。スレッドは、
JVMPI_CallTrace構造体のenv_idフィールドによって識別されます。プロファイラエージェントは、JVMPI_CallTrace構造体に、要求されたスタックの深さに十分なメモリを割り当てる必要があります。VM は、framesバッファおよびnum_framesフィールドに情報を入れます。引数:
trace- VM によって情報が入れられるトレースデータ構造体 depth- 呼び出しスタックトレースの深さ
jlong (*GetCurrentThreadCpuTime)(void);
- プロファイラエージェントによって呼び出され、現在のスレッドによって消費された累積 CPU 時間を取得します。
戻り値:
ナノ秒単位の時間
jobjectID (*GetMethodClass)(jmethodID mid);
- プロファイラエージェントによって呼び出され、メソッドを定義しているクラスのオブジェクト ID を取得します。
プロファイラは、この関数を呼び出す前に GC を無効にする必要があります。
引数:
mid- メソッド ID 戻り値:
定義しているクラスのオブジェクト ID
void * (*GetThreadLocalStorage)(JNIEnv *env_id);
- プロファイラによって呼び出され、JVMPI のスレッドローカルな記憶領域の値を取得します。JVMPI は、エージェントに対して、スレッドごとのプロファイリング情報を記録するために利用できる、ポインタサイズのスレッドローカルな記憶領域を提供します。
引数:
env_id- スレッドの JNIEnv *戻り値:
スレッドローカルな記憶領域の値
jobjectID (*GetThreadObject)(JNIEnv *env);
- プロファイラエージェントによって呼び出され、
JNIEnvポインタに対応するスレッドオブジェクト ID を取得します。プロファイラは、この関数を呼び出す前に GC を無効にする必要があります。
引数:
env- - スレッドの JNIEnvポインタ戻り値:
スレッドのオブジェクト ID
jint (*GetThreadStatus)(JNIEnv *env);
- プロファイラエージェントによって呼び出され、スレッドのステータスを取得します。
引数:
env- スレッドの JNIEnv *戻り値:
JVMPI_THREAD_RUNNABLE- スレッドは実行可能 JVMPI_THREAD_MONITOR_WAIT- スレッドはモニターを待機している JVMPI_THREAD_CONDVAR_WAIT- スレッドは条件変数を待機している スレッドが (
java.lang.Thread.suspend、SuspendThread、またはSuspendThreadListによって) 中断されているか、上記のいずれかの状態によって割り込まれているときは、JVMPI_THREAD_SUSPENDEDまたはJVMPI_THREAD_INTERRUPTEDビットがセットされます。
void (*NotifyEvent)(JVMPI_Event *event);
- VM によって呼び出され、プロファイリングエージェントにイベントを送信します。プロファイラエージェントは、
EnableEventを呼び出すことによって関心のあるイベントを登録するか、またはRequestEventを呼び出すことによって特定の型のイベントを要求します。
EnableEventによってイベントが有効にされた場合は、イベントを生成したスレッドでイベントが送信されます。RequestEventによってイベントが要求された場合は、イベントを要求したスレッドでイベントが送信されます。複数のスレッドが複数のイベントを同時に送信することがあります。イベント特有の情報に
jobjectIDが含まれている場合、この関数は、GC が無効な状態で呼び出されます。GC は、関数からの復帰後に有効になります。
JVMPI_Event構造体およびイベント特有の情報に割り当てられた領域は、この関数からの復帰後に、仮想マシンによって解放されます。プロファイラエージェントは、残しておく必要のあるデータを内部バッファにコピーする必要があります。引数:
event- VM からプロファイリングエージェントに送信される JVMPI イベント
void (*ProfilerExit)(jint err_code);
- プロファイラエージェントによって呼び出され、エラーコードに
err_codeを設定した状態でプロファイラが終了することを VM に通知します。この関数により、VM も同じerr_codeで終了します。引数:
err_code- 終了コード
JVMPI_RawMonitor (*RawMonitorCreate)(char *lock_name);
- プロファイラによって呼び出され、raw モニターを作成します。
raw モニターは、Java モニターと似ています。しかし、raw モニターは Java オブジェクトに関連付けられていない点が異なります。
プロファイラエージェントが、スレッド中断モードでこの関数を呼び出すことは安全ではありません。それは、この関数が
mallocなどの任意のシステム関数を呼び出して、内部システムライブラリロックをブロックする可能性があるからです。raw モニターがアンダースコア (
_) で始まる名前で作成された場合は、そのモニターのコンテンションイベントはプロファイラエージェントに送信されません。引数:
lock_name- raw モニターの名前 戻り値:
raw モニター
void (*RawMonitorDestroy)(JVMPI_RawMonitor lock_id);
- プロファイラエージェントによって呼び出され、raw モニターを破棄し、そのモニターに関連付けられているすべてのシステムリソースを解放します。
raw モニターは、Java モニターと似ています。しかし、raw モニターは Java オブジェクトに関連付けられていない点が異なります。
プロファイラエージェントが、スレッド中断モードでこの関数を呼び出すことは安全ではありません。それは、この関数が
freeなどの任意のシステム関数を呼び出して、内部システムライブラリロックをブロックする可能性があるからです。引数:
lock_id- 破棄する raw モニター
void (*RawMonitorEnter)(JVMPI_RawMonitor lock_id);
- プロファイラエージェントによって呼び出され、raw モニターに入ります。
raw モニターは、Java モニターと似ています。しかし、raw モニターは Java オブジェクトに関連付けられていない点が異なります。
プロファイラエージェントが、スレッド中断モードでこの関数を呼び出すことは安全ではありません。それは、現在のスレッドが、すでに中断されているスレッドの 1 つによって獲得されている raw モニターをブロックする可能性があるからです。
引数:
lock_id- 入る raw モニター
void (*RawMonitorExit)(JVMPI_RawMonitor lock_id);
- プロファイラエージェントによって呼び出され、raw モニターから出ます。
raw モニターは、Java モニターと似ています。しかし、raw モニターは Java オブジェクトに関連付けられていない点が異なります。
引数:
lock_id- 出る raw モニター
void (*RawMonitorNotifyAll)(JVMPI_RawMonitor lock_id);
- プロファイラによって呼び出され、raw モニターを待機しているすべてのスレッドに通知を送ります。
raw モニターは、Java モニターと似ています。しかし、raw モニターは Java オブジェクトに関連付けられていない点が異なります。
引数:
lock_id- 通知する raw モニター
void (*RawMonitorWait)(JVMPI_RawMonitor lock_id, jlong ms);
- プロファイラエージェントによって呼び出され、指定したタイムアウトまでの期間、raw モニターを待機します。タイムアウト期間に 0 を指定すると、スレッドはずっと待機し続けます。
raw モニターは、Java モニターと似ています。しかし、raw モニターは Java オブジェクトに関連付けられていない点が異なります。
Hotspot に関する注意:
RawMonitorWaitを実行しているスレッドが、指定された raw モニターを所有していない場合、待機は発生しません。引数:
lock_id- 待機対象の raw モニター ms- 待機する時間 (ミリ秒)
jint (*RequestEvent)(jint event_type, void *arg);
- プロファイラエージェントによって呼び出され、特定の型のイベントを通知することを要求します。プロファイラエージェントは、event_type のほかに、特定のイベント型に特有の補足情報を提供する引数を渡すことも可能です。
この関数を呼び出すと、
JVMPI_EVENT_HEAP_DUMP、JVMPI_EVENT_MONITOR_DUMP、JVMPI_EVENT_OBJECT_DUMPなど、1 回かぎりのイベントを要求できます。これらのイベントの通知は、EnableEvent関数とDisableEvent関数では制御できません。また、この関数を呼び出すと、特定のクラス、スレッド、またはオブジェクトの「定義イベント」を要求できます。これは、プロファイラエージェントがイベントで受け取った未知のクラス、メソッド、スレッド、またはオブジェクト ID を解釈する必要があるにもかかわらず、対応する定義イベントが無効にされていた、という場合に便利です。
- プロファイラエージェントが未知のクラス ID についての情報を受け取るためには、
JVMPI_EVENT_CLASS_LOADイベントを要求し、イベント特有の引数をそのクラスのオブジェクト ID に設定する- プロファイラエージェントが未知のスレッド ID についての情報を受け取るためには、
JVMPI_EVENT_THREAD_STARTイベントを要求し、イベント特有の引数をそのスレッドのオブジェクト ID に設定する- プロファイラエージェントが未知のオブジェクト ID についての情報を受け取るためには、
JVMPI_EVENT_OBJECT_ALLOCイベントを要求し、イベント特有の引数をそのオブジェクトの ID に設定するこのため、プロファイラエージェントは
EnableEventを呼び出して上の 3 つのイベントを非同期的に有効にするか、またはRequestEventを呼び出してこれらのイベントを同期的に要求することができます。要求されたイベントは、RequestEvent呼び出しを発行したのと同じスレッドで、RequestEvent関数から復帰する前に送信されます。
RequestEvent関数は、上記以外のイベントを要求するためには使用できません。
RequestEventによって要求されたイベントは、event_typeでJVMPI_REQUESTED_EVENTビットがセットされた状態で着信します。引数:
event_type- イベントの型。 JVMPI_EVENT_CLASS_LOADなどarg- イベントに特有の引数 戻り値:
JVMPI_SUCCESS- 要求が成功 JVMPI_FAIL- 要求が失敗 JVMPI_NOT_AVAILABLE- 要求された event_typeの発行がサポートされていない
void (*ResumeThread)(JNIEnv *env);
- プロファイラエージェントによって呼び出され、スレッドを再開します。
java.lang.Thread.suspendメソッドによって中断されたスレッドは、JVMPI のResumeThread関数で再開できます。引数:
env- スレッドの JNIEnv *
void (*ResumeThreadList)(jint reqCount, JNIEnv **reqList, jint *results);
- プロファイラエージェントによって呼び出され、reqList 配列で指定された reqCount スレッドを再開します。
引数:
reqCount- 再開するスレッドの数reqList- 再開するスレッドのリストresults- スレッドごとの再開結果のリスト戻り値:
再開が成功した場合はゼロ (0)、失敗した場合はゼロ以外の値が、指定したスレッドの結果配列要素に含まれます。
void (*RunGC)(void);
- プロファイラによって呼び出され、完全なガベージコレクションを強制的に実行します。この関数は、GC が無効な状態で呼び出してはなりません。
void (*SetThreadLocalStorage)(JNIEnv *env_id, void *ptr);
- プロファイラエージェントによって呼び出され、JVMPI のスレッドローカルな記憶領域の値を設定します。JVMPI は、エージェントに対して、スレッドごとのプロファイリング情報を記録するために利用できる、ポインタサイズのスレッドローカルな記憶領域を提供します。
引数:
env_id- スレッドの JNIEnv *ptr- スレッドローカルな記憶領域に入力する値
void (*SuspendThread)(JNIEnv *env);
- プロファイラエージェントによって呼び出され、スレッドを中断します。この関数が呼び出されると、システムは、スレッド中断モードに入ります。
JVMPI の
SuspendThread関数によって中断されたスレッドは、java.lang.Thread.resumeメソッドで再開できます。JDK の実装では、この関数は、GC が無効な状態で呼び出す必要があります。GC は、すべてのスレッドが再開されるまで無効でなければならない
引数:
env- スレッドの JNIEnv *
void (*SuspendThreadList)(jint reqCount, JNIEnv **reqList, jint *results);
- プロファイラエージェントによって呼び出され、reqList 配列で指定された reqCount スレッドを中断します。この関数が呼び出されると、システムは、スレッド中断モードに入ります。
JDK の実装では、この関数は、GC が無効な状態で呼び出す必要があります。GC は、すべてのスレッドが再開されるまで無効でなければならない
引数:
reqCount- 中断するスレッドの数reqList- 中断するスレッドのリストresults- スレッドごとの中断結果のリスト戻り値:
中断が成功した場合はゼロ (0)、失敗した場合はゼロ以外の値が、指定したスレッドの結果配列要素に含まれます。
jboolean (*ThreadHasRun)(JNIEnv *env);
- プロファイラによって呼び出され、特定の
JNIEnvポインタによって識別されたスレッドが、SuspendThreadまたはSuspendThreadListによって最後に中断されたとき以降に CPU 時間を消費したかどうかを特定します。この関数は、スレッドがResumeThreadまたはResumeThreadListによって再開されたあと、SuspendThreadまたはSuspendThreadList関数によって再度中断されたときに呼び出す必要があります。引数:
env- スレッドの JNIEnv *戻り値:
JNI_TRUE- スレッドは実行される機会があった JNI_FALSE- スレッドは実行される機会がなかった
jobject (*jobjectID2jobject)(jobjectID jid);
- プロファイラエージェントによって呼び出され、オブジェクト ID を JNI ハンドルに変換します。
この関数は試験用で、JVMPI の最終仕様からは削除されるかもしれません。これを使用した場合の安全性は確保されておらず、プロファイラの信頼性が下がる可能性があります。注も参照してください。
引数:
jid- 変換対象のオブジェクト ID 戻り値:
JNI ハンドル
jobjectID (*jobject2jobjectID)(jobject j);
- プロファイラエージェントによって呼び出され、JNI ハンドルをオブジェクト ID に変換します。
この関数は試験用で、JVMPI の最終仕様からは削除されるかもしれません。これを使用した場合の安全性は確保されておらず、プロファイラの信頼性が下がる可能性があります。
引数:
j- JNI ハンドル 戻り値:
オブジェクト ID
JNI 関数を任意の JVMPI イベントハンドラ内で呼び出すのは危険です。JVMPI イベントは、仮想マシンが JNI 関数の実行に適していない状態のときに発行される可能性があります。プロファイラエージェントが JNI 関数を呼び出してもよいのは、マルチスレッドモード (JVMPI 仕様の定義による) のときだけです。しかも、競合状態、デッドロック、および無限の再帰が発生しないように、最大限の注意を払わなければなりません。
jobjectID2jobject および jobject2jobjectID を呼び出してもよいのは、GC が無効なモード (JVMPI 仕様の定義による) のときだけです。そのモードで JNI 関数を呼び出すのは危険です。したがって、GC が無効なモードで動作しているイベントハンドラ内で jobjectID2jobject を呼び出し、その結果の jobject を JNI 関数を使って処理する、ということは絶対に避けてください。
3. イベント
- JVMPI_EVENT_ARENA_DELETE
- JVMPI_EVENT_ARENA_NEW
- JVMPI_EVENT_CLASS_LOAD
- JVMPI_EVENT_CLASS_LOAD_HOOK
- JVMPI_EVENT_CLASS_UNLOAD
- JVMPI_EVENT_COMPILED_METHOD_LOAD
- JVMPI_EVENT_COMPILED_METHOD_UNLOAD
- JVMPI_EVENT_DATA_DUMP_REQUEST
- JVMPI_EVENT_DATA_RESET_REQUEST
- JVMPI_EVENT_GC_FINISH
- JVMPI_EVENT_GC_START
- JVMPI_EVENT_HEAP_DUMP
- JVMPI_EVENT_JNI_GLOBALREF_ALLOC
- JVMPI_EVENT_JNI_GLOBALREF_FREE
- JVMPI_EVENT_JNI_WEAK_GLOBALREF_ALLOC
- JVMPI_EVENT_JNI_WEAK_GLOBALREF_FREE
- JVMPI_EVENT_JVM_INIT_DONE
- JVMPI_EVENT_JVM_SHUT_DOWN
- JVMPI_EVENT_METHOD_ENTRY
- JVMPI_EVENT_METHOD_ENTRY2
- JVMPI_EVENT_METHOD_EXIT
- JVMPI_EVENT_MONITOR_CONTENDED_ENTER
- JVMPI_EVENT_MONITOR_CONTENDED_ENTERED
- JVMPI_EVENT_MONITOR_CONTENDED_EXIT
- JVMPI_EVENT_MONITOR_DUMP
- JVMPI_EVENT_MONITOR_WAIT
- JVMPI_EVENT_MONITOR_WAITED
- JVMPI_EVENT_OBJECT_ALLOC
- JVMPI_EVENT_OBJECT_DUMP
- JVMPI_EVENT_OBJECT_FREE
- JVMPI_EVENT_OBJECT_MOVE
- JVMPI_EVENT_RAW_MONITOR_CONTENDED_ENTER
- JVMPI_EVENT_RAW_MONITOR_CONTENDED_ENTERED
- JVMPI_EVENT_RAW_MONITOR_CONTENDED_EXIT
- JVMPI_EVENT_THREAD_END
- JVMPI_EVENT_THREAD_START
- JVMPI_EVENT_INSTRUCTION_START
JVMPI_EVENT_ARENA_DELETE
- ヒープ領域が削除される時点で送信されます。
この領域内のすべてのオブジェクトが解放されます。これらのオブジェクトについて、明示的な
JVMPI_EVENT_OBJECT_FREEは送信されません。プロファイラエージェントは、領域内のオブジェクトの割り当て、および領域へのオブジェクトの出入りを記録することによって、領域内に現在存在するすべてのオブジェクトを推測できます。このイベントは、スレッド中断モードで発行されます。プロファイラは、モニターに入ったり、C ヒープから割り当てたり (
mallocなどによる) する、ブロックする呼び出しを実行してはなりません。このイベントは常に、
JVMPI_EVENT_GC_STARTイベントと、対応するJVMPI_EVENT_GC_FINISHイベントの間で送信されます。プロファイラエージェントは、このイベントを処理するのに必要なすべてのロックを、JVMPI_EVENT_GC_STARTのイベントハンドラ内で獲得する必要があります。内容:struct { jint arena_id; } delete_arena;
arena_id- 削除される領域の ID
JVMPI_EVENT_ARENA_NEW
- オブジェクトを割り当てる新しい領域が作成される時点で送信されます。
内容:struct { jint arena_id; char *arena_name; } new_arena;
arena_id- 領域に与えられた ID arena_name- 領域の名前
JVMPI_EVENT_CLASS_LOAD
- VM にクラスがロードされる時点、またはプロファイラエージェントが
RequestEventを呼び出してJVMPI_EVENT_CLASS_LOADイベントを要求した時点で送信されます。後者の場合は、イベント型でJVMPI_REQUESTED_EVENTビットがセットされます。このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。struct { char *class_name; char *source_name; jint num_interfaces; jint num_methods; JVMPI_Method *methods; jint num_static_fields; JVMPI_Field *statics; jint num_instance_fields; JVMPI_Field *instances; jobjectID class_id; } class_load;内容:
class_name- ロードされているクラスの名前 source_name- クラスを定義しているソースファイルの名前 num_interfaces- このクラスによって実装されるインタフェースの数 methods- クラス内で定義されたメソッド num_static_fields- このクラス内で定義された static フィールドの数 statics- このクラス内で定義された static フィールド num_instance_fields- このクラス内で定義されたインスタンスフィールドの数 instances- このクラス内で定義されたインスタンスフィールド class_id- クラスのオブジェクト ID 注: クラス ID は、クラスオブジェクトの ID で、
JVMPI_EVENT_OBJECT_MOVEの着信時に変更されます。
JVMPI_EVENT_CLASS_LOAD_HOOK
- VM がクラスファイルデータを取得したとき、そのクラスのメモリ内部表現を構築する前の時点で送信されます。プロファイラエージェントは、VM によって送信された既存のクラスファイルデータに、プロファイリングフックを計測することができます。
プロファイラは、このイベントで送信されるメモリ割り当て関数のポインタを使って、修正したクラスファイルデータのバッファ用の領域を割り当てる必要があります。新しいクラスファイルデータのバッファを解放する処理は、VM によって実行されるからです。
struct { unsigned char *class_data; jint class_data_len; unsigned char *new_class_data; jint new_class_data_len; void * (*malloc_f)(unsigned int); } class_load_hook;内容:
class_data- 現在のクラスファイルデータのバッファへのポインタ class_data_len- 現在のクラスファイルデータのバッファの長さ new_class_data- 修正されたクラスファイルデータのバッファへのポインタ new_class_data_len- 新しいクラスファイルデータのバッファの長さ malloc_f- メモリ割り当て関数へのポインタ プロファイラエージェントは、
NotifyEventから復帰する前に、修正した新しいクラスファイルデータのバッファを指すようにnew_class_dataを設定し、new_class_data_lenにそのバッファの長さを設定する必要があります。このクラスに修正を加えないことにした場合は、new_class_dataとnew_class_data_lenの両方に、古い値を設定する必要があります。
JVMPI_EVENT_CLASS_UNLOAD
- クラスがアンロードされる時点で送信されます。
このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。struct { jobjectID class_id; } class_unload;内容:
class_id- アンロードされるクラス
JVMPI_EVENT_COMPILED_METHOD_LOAD
- メソッドがコンパイルされ、メモリ内にロードされる時点で送信されます。
struct { jmethodID method_id; void *code_addr; jint code_size; jint lineno_table_size; JVMPI_Lineno *lineno_table; } compiled_method_load;内容:
method_id- コンパイルおよびロードされているメソッド code_addr- コンパイルされたメソッドコードがロードされるアドレス code_size- コンパイルされたコードのサイズ lineno_table_size- 行番号テーブルのサイズ lineno_table- メソッドの先頭からのオフセットをソースファイルの行番号にマッピングするテーブル
JVMPI_EVENT_COMPILED_METHOD_UNLOAD
- コンパイルされたメソッドがメモリからアンロードされる時点で送信されます。
struct { jmethodID method_id; } compiled_method_unload;内容:
method_id- アンロードされる、コンパイルされたメソッド
JVMPI_EVENT_DATA_DUMP_REQUEST
- VM によって送信され、プロファイラエージェントにデータをダンプするよう要求します。これは単に示唆しているだけであり、プロファイラエージェントはこのイベントに必ずしも反応する必要はありません。これは、ユーザからのコマンド行シグナルを処理する場合に便利です。たとえば、JDK では、Microsoft Windows 上で Ctrl+Break キー、Solaris 上で Ctrl+\ キーを押すと、VM はこのイベントをプロファイラエージェントに送信します。
イベント特有の情報はありません。
JVMPI_EVENT_DATA_RESET_REQUEST
- VM によって送信され、プロファイラエージェントにデータをリセットするように要求します。これは単に示唆しているだけであり、プロファイラエージェントはこのイベントに必ずしも反応する必要はありません。これは、ユーザからのコマンド行シグナルを処理する場合に便利です。たとえば、JDK では、Microsoft Windows 上で Ctrl+Break キー、Solaris 上で Ctrl+\ キーを押すと、VM はこのイベントをプロファイラエージェントに送信します。
イベント特有の情報はありません。
JVMPI_EVENT_GC_FINISH
- GC の終了時に送信されます。プロファイラエージェントは、オブジェクトの解放イベント、オブジェクトの移動イベント、および領域の削除イベントを処理するために GC の開始通知の処理中に獲得したすべてのロックを、このイベントを処理する際に解放できます。このイベントのあとに、システムはマルチスレッドモードに戻ります。
イベント特有のデータには、Java ヒープ統計が入っています。
内容:struct { jlong used_objects; jlong used_object_space; jlong total_object_space; } gc_info;
used_objects- ヒープ上で使われていたオブジェクトの数 used_object_space- オブジェクトによって使用されていた領域の量 (バイト単位) total_object_space- オブジェクト領域の合計量 (バイト単位)
JVMPI_EVENT_GC_START
- GC を開始しようとしている時点で送信されます。このイベントのあとに、システムはスレッド中断モードに入ります。デッドロックを避けるため、プロファイラエージェントは、オブジェクトの解放イベント、オブジェクトの移動イベント、および領域の削除イベントを処理するのに必要なすべてのロックを、このイベントのイベントハンドラ内で獲得する必要があります。
イベント特有の情報はありません。
JVMPI_EVENT_HEAP_DUMP
RequestEvent関数によって要求されたときに送信されます。プロファイラエージェントはRequestEventに、第 2 引数としてJVMPI_HeapDumpArg構造体を渡し、heap_dump_level フィールドにダンプレベルを設定することによって、ダンプする情報のレベルを指定できます。ダンプレベルとしては、次の値のどれかを指定できます。
JVMPI_DUMP_LEVEL_0JVMPI_DUMP_LEVEL_1JVMPI_DUMP_LEVEL_2null 値が渡された場合、ダンプレベルは
JVMPI_DUMP_LEVEL_2に設定されます。このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。イベント特有のデータには、Java ヒープ内のすべてのライブオブジェクトのスナップショットが入っています。
内容:struct { int dump_level; char *begin; char *end; jint num_traces; JVMPI_CallTrace *traces; } heap_dump;
dump_level- RequestEventで指定されたダンプレベルbegin- ヒープのダンプの先頭 end- ヒープのダンプの末尾 num_traces- GC のルートが存在するスタックトレースの数。JVMPI_DUMP_LEVEL_0 の場合は 0 traces- GC のルートが存在するスタックトレース
beginとendの間に置かれるヒープダンプの形式は、要求された情報のレベルによって異なります。この形式の詳細については、「ダンプの形式」の項を参照してください。
JVMPI_EVENT_JNI_GLOBALREF_ALLOC
- JNI グローバル参照が作成される時点で送信されます。イベント特有のデータには、JNI グローバル参照と、対応するオブジェクト ID が入っています。
このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。内容:struct { jobjectID obj_id; jobject ref_id; } jni_globalref_alloc;
obj_id- グローバル参照が参照するオブジェクト ID ref_id- JNI グローバル参照
JVMPI_EVENT_JNI_GLOBALREF_FREE
- JNI グローバル参照が削除される時点で送信されます。イベント特有のデータには、削除される JNI グローバル参照が入っています。
内容:struct { jobject ref_id; } jni_globalref_free;
ref_id- JNI グローバル参照
JVMPI_EVENT_JNI_WEAK_GLOBALREF_ALLOC
- JNI グローバル弱参照が作成される時点で送信されます。イベント特有のデータには、JNI グローバル弱参照と、対応するオブジェクト ID が入っています。
このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。内容:struct { jobjectID obj_id; jobject ref_id; } jni_globalref_alloc;
obj_id- グローバル弱参照が参照するオブジェクト ID ref_id- JNI グローバル弱参照
JVMPI_EVENT_JNI_WEAK_GLOBALREF_FREE
- JNI グローバル弱参照が削除される時点で送信されます。イベント特有のデータには、削除される JNI グローバル弱参照が入っています。
内容:struct { jobject ref_id; } jni_globalref_free;
ref_id- JNI グローバル弱参照
JVMPI_EVENT_JVM_INIT_DONE
- VM の初期化が実行されるときに VM によって送信されます。
CreateSystemThreadを呼び出して安全なのは、このイベントが通知されたあとだけです。イベント特有のデータはありません。
JVMPI_EVENT_JVM_SHUT_DOWN
- VM がシャットダウンしているときに VM によって送信されます。プロファイラは通常、プロファイリングデータを保存することによって、このイベントに応答します。
イベント特有のデータはありません。
JVMPI_EVENT_METHOD_ENTRY
- メソッドに入る時点で送信されます。
JVMPI_EVENT_METHOD_ENTRY2とは違って、このイベントは、メソッドの呼び出し対象であるターゲットオブジェクトのjobjectIDを送信しません。struct { jmethodID method_id; } method;内容:
method_id- 出ようとしているメソッド
JVMPI_EVENT_METHOD_ENTRY2
- メソッドに入る時点で送信されます。メソッドがインスタンスメソッドの場合は、イベントと一緒にターゲットオブジェクトの
jobjectIDが送信されます。メソッドが static メソッドの場合は、このイベントのobj_idフィールドがnullに設定されます。このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。struct { jmethodID method_id; jobjectID obj_id; } method_entry2;内容:
method_id- 出ようとしているメソッド obj_id- ターゲットオブジェクト。static メソッドの場合は null
JVMPI_EVENT_METHOD_EXIT
- メソッドから出る時点で送信されます。メソッドから出るとは、通常に終了する場合か、または処理されない例外が発生した場合を指します。
struct { jmethodID method_id; } method;内容:
method_id- 出ようとしているメソッド
JVMPI_EVENT_MONITOR_CONTENDED_ENTER
- スレッドが Java モニターに入ろうとしたとき、そのモニターがすでに別のスレッドによって獲得されている場合に送信されます。
このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。内容:struct { jobjectID object; } monitor;
object- モニターに関連付けられたオブジェクト ID
JVMPI_EVENT_MONITOR_CONTENDED_ENTERED
- 別のスレッドが Java モニターを解放するのを待ったあとで、スレッドがその Java モニターに入るときに送信されます。
このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。内容:struct { jobjectID object; } monitor;
object- モニターに関連付けられたオブジェクト ID
JVMPI_EVENT_MONITOR_CONTENDED_EXIT
- スレッドが Java モニターから出たとき、別のスレッドが同じモニターを獲得するために待機している場合に送信されます。
このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。内容:struct { jobjectID object; } monitor;
object- モニターに関連付けられたオブジェクト ID
JVMPI_EVENT_MONITOR_DUMP
RequestEvent関数によって要求されたときに送信されます。イベント特有のデータには、VM 内のすべてのスレッドおよびモニターのスナップショットが入っています。
このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。内容:struct { char *begin; char *end; jint num_traces; JVMPI_CallTrace *traces; jint *threads_status; } monitor_dump;
begin- モニターのダンプバッファの先頭 end- ダンプバッファの末尾 num_traces- スレッドトレースの数 traces- すべてのスレッドのトレース thread_status- すべてのスレッドのステータス モニターのダンプバッファ形式の詳細については、「ダンプの形式」の項を参照してください。
JVMPI_EVENT_MONITOR_WAIT
- スレッドがオブジェクトを待機しようとしているときに送信されます。
このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。内容:struct { jobjectID object; jlong timeout; } monitor_wait;
object- 現在のスレッドが待機しようとしているオブジェクトの ID ( nullは、スレッドがThread.sleepであることを示す)timeout- スレッドが待機する時間 (単位はミリ秒)。0 は、ずっと待機し続けることを示す 注: イベントの object フィールドで null が見つかった場合、エージェントは、イベントが
Thread.sleep()呼び出しでポストされたことを推測できます。しかし、Thread.sleep()はモニター経由で実装される必要がないため、Thread.sleep()がこのイベントをポストする必要はありません。
JVMPI_EVENT_MONITOR_WAITED
- スレッドがオブジェクトの待機を終了するときに送信されます。
このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。内容:struct { jobjectID object; jlong timeout; } monitor_wait;
object- 現在のスレッドが待機しているオブジェクト ID ( nullは、スレッドがThread.sleepであることを示す)timeout- スレッドが待機した時間 (単位はミリ秒) 注: イベントの object フィールドで null が見つかった場合、エージェントは、イベントが
Thread.sleep()呼び出しでポストされたことを推測できます。しかし、Thread.sleep()はモニター経由で実装される必要がないため、Thread.sleep()がこのイベントをポストする必要はありません。
JVMPI_EVENT_OBJECT_ALLOC
- オブジェクトが割り当てられるか、またはプロファイラエージェントが
RequestEvent呼び出しを発行することによって、JVMPI_EVENT_OBJECT_ALLOCイベントを要求するときに送信されます。後者の場合は、イベント型でJVMPI_REQUESTED_EVENTビットがセットされます。このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。struct { jint arena_id; jobjectID class_id; jint is_array; jint size; jobjectID obj_id; } obj_alloc;内容:
arena_id- 割り当てられる領域 class_id- このオブジェクトが属するクラス、または is_arrayがJVMPI_CLASSの場合は、配列要素のクラスis_array- 値は次のいずれか
JVMPI_NORMAL_OBJECT通常のオブジェクト JVMPI_CLASSオブジェクトの配列 JVMPI_BOOLEANboolean 型の配列 JVMPI_BYTEbyte 型の配列 JVMPI_CHARchar 型の配列 JVMPI_SHORTshort 型の配列 JVMPI_INTint 型の配列 JVMPI_LONGlong 型の配列 JVMPI_FLOATfloat 型の配列 JVMPI_DOUBLEdouble 型の配列 size- サイズ (単位はバイト) obj_id- 一意のオブジェクト ID
JVMPI_EVENT_OBJECT_DUMP
RequestEvent関数によって要求されたときに送信されます。ダンプが要求されているオブジェクトのjobjectIDは、第 2 引数としてRequestEventに渡されているはずです。プロファイラエージェントは、GC が無効な状態でこのイベントを要求する必要があります。
イベント特有のデータには、オブジェクトのスナップショットが入っています。
内容:struct { jint data_len; char *data; } object_dump;
data_len- オブジェクトのダンプバッファの長さ data- オブジェクトダンプの先頭 オブジェクトダンプバッファの形式の詳細については、「ダンプの形式」の項を参照してください。
JVMPI_EVENT_OBJECT_FREE
- オブジェクトが解放される時点で送信されます。
このイベントは、スレッド中断モードで発行されます。プロファイラは、モニターに入ったり、C ヒープから割り当てたり (
mallocなどによる) する、ブロックする呼び出しを実行してはなりません。このイベントは常に、
JVMPI_EVENT_GC_STARTイベントと、対応するJVMPI_EVENT_GC_FINISHイベントの間で送信されます。プロファイラエージェントは、このイベントを処理するのに必要なすべてのロックを、JVMPI_EVENT_GC_STARTのイベントハンドラ内で獲得する必要があります。struct { jobjectID obj_id; } obj_free;内容:
obj_id- 解放されるオブジェクト
JVMPI_EVENT_OBJECT_MOVE
- ヒープ内でオブジェクトが移動される時点で送信されます。
このイベントは、スレッド中断モードで発行されます。プロファイラは、モニターに入ったり、C ヒープから割り当てたり (
mallocなどによる) する、ブロックする呼び出しを実行してはなりません。このイベントは常に、
JVMPI_EVENT_GC_STARTイベントと、対応するJVMPI_EVENT_GC_FINISHイベントの間で送信されます。プロファイラエージェントは、このイベントを処理するのに必要なすべてのロックを、JVMPI_EVENT_GC_STARTのイベントハンドラ内で獲得する必要があります。struct { jint arena_id; jobjectID obj_id; jint new_arena_id; jobjectID new_obj_id; } obj_move;内容:
arena_id- 現在の領域 obj_id- 現在のオブジェクト ID new_arena_id- 新しい領域 new_obj_id- 新しいオブジェクト ID
JVMPI_EVENT_RAW_MONITOR_CONTENDED_ENTER
- スレッドが raw モニターに入ろうとしたとき、そのモニターがすでに別のスレッドによって獲得されている場合に送信されます。
内容:struct { char *name; JVMPI_RawMonitor id; } raw_monitor;
name- raw モニターの名前 id- raw モニターの ID
JVMPI_EVENT_RAW_MONITOR_CONTENDED_ENTERED
- 別のスレッドが raw モニターを解放するのを待ったあとで、スレッドがその raw モニターに入るときに送信されます。
内容:struct { char *name; JVMPI_RawMonitor id; } raw_monitor;
name- raw モニターの名前 id- raw モニターの ID
JVMPI_EVENT_RAW_MONITOR_CONTENDED_EXIT
- スレッドが raw モニターから出たとき、別のスレッドが同じモニターを獲得するために待機している場合に送信されます。
内容:struct { char *name; JVMPI_RawMonitor id; } raw_monitor;
name- raw モニターの名前 id- raw モニターの ID
JVMPI_EVENT_THREAD_END
- VM 内のスレッドが終了する時点で送信されます。
このイベント通知で受け取る
JVMPI_Eventの env_id フィールドは、終了したスレッドのJNIEnvインタフェースポインタです。
JVMPI_EVENT_THREAD_START
- VM 内でスレッドが開始される時点か、またはプロファイラエージェントが
RequestEventを呼び出してJVMPI_EVENT_THREAD_STARTイベントを要求した時点で送信されます。後者の場合は、イベント型でJVMPI_REQUESTED_EVENTビットがセットされます。このイベントは、GC が無効な状態で発行されます。GC は、
NotifyEventから復帰したあとに、再度有効になります。struct { char *thread_name; char *group_name; char *parent_name; jobjectID thread_id; JNIEnv *thread_env_id; } thread_start;内容:
スレッドには、
thread_name- 開始されるスレッドの名前 group_name- スレッドが属するグループ parent_name- 親の名前 thread_id- スレッドのオブジェクト ID thread_env_id- スレッドの JNIEnv *JNIEnvポインタとスレッドオブジェクト ID が関連付けられています。JVMPI は、JNIEnvポインタをスレッド ID として使います。
JVMPI_EVENT_INSTRUCTION_START
- 各命令 (バイトコード操作) がインタプリタによって発行される時点で送信されます。
このイベントは、コンパイルされたコードではなく、インタプリタによってのみ発行されます。
struct { jmethodID method_id; jint offset; union { struct { jboolean is_true; } if_info; struct { jint key; jint low; jint hi; } tableswitch_info; struct { jint chosen_pair_index; /* actually chosen pair index (0-based) */ jboolean is_default; /* whether default branch is taken */ } lookupswitch_info; } u; } instruction;内容:
method_id- 命令を実行するメソッドの ID offset- メソッドのバイトコード内での命令のオフセット is_true- if バイトコードで true または false の分岐がとられるかどうかを示す key- tableswitch内でインデックスとして使われる最上位のスタック値low- tableswitch内のインデックスの最小値hi- tableswitch内のインデックスの最大値chosen_pair_index- lookupswitch内で実際に選択されたペアの、ゼロから始まるインデックスis_default- lookupswitch内でデフォルトの分岐がとられるかどうかを示すHotspot に関する注意:
-XX:+EnableJVMPIInstructionStartEventフラグを使って実行してください。そうしないと、このイベントは送信されません。4. ダンプの形式
4.1. ダンプ形式の説明で使われているサイズと型
u1:1 バイト u2:2 バイト u4:4 バイト u8:8 バイト ty:u1 で、次の値をとる
JVMPI_NORMAL_OBJECT通常のオブジェクト JVMPI_CLASSオブジェクトの配列 JVMPI_BOOLEANboolean 型の配列 JVMPI_BYTEbyte 型の配列 JVMPI_CHARchar 型の配列 JVMPI_SHORTshort 型の配列 JVMPI_INTint 型の配列 JVMPI_LONGlong 型の配列 JVMPI_FLOATfloat 型の配列 JVMPI_DOUBLEdouble 型の配列 vl:値。正確なサイズは値の型によって異なる
boolean、byteu1 short、charu2 int、floatu4 long、doubleu8 JNIEnv *、jobjectID、およびJVMPI_RawMonitorsizeof(void *)
ヒープのダンプ形式は、要求された情報のレベルによって異なります。
JVMPI_DUMP_LEVEL_0:
ダンプは、次の形式の一連のレコードで構成されます。
tyオブジェクトの型 jobjectIDオブジェクト
JVMPI_DUMP_LEVEL_1:
ダンプの形式は、JVMPI_DUMP_LEVEL_2 のダンプの形式と基本的に同じですが、オブジェクトインスタンスダンプのプリミティブフィールド、クラスダンプのプリミティブ static フィールド、およびプリミティブ配列要素の値はダンプに出力されません。
JVMPI_DUMP_LEVEL_2:
ダンプは、一連のレコードで構成され、各レコードには、8 ビットのレコード型と、そのあとに各レコード型に固有の形式のデータが含まれています。
レコード型 レコードデータ JVMPI_GC_ROOT_UNKNOWN
(未知のルート)
jobjectIDオブジェクト JVMPI_GC_ROOT_JNI_GLOBAL
(JNI グローバル参照ルート)
jobjectIDオブジェクト jobjectJNI グローバル参照 JVMPI_GC_ROOT_JNI_LOCAL
(JNI ローカル参照)
jobjectIDオブジェクト JNIEnv *スレッド u4スタックトレース内のフレーム番号 (空の場合は -1) JVMPI_GC_ROOT_JAVA_FRAME
(Java スタックフレーム)
jobjectIDオブジェクト JNIEnv *スレッド u4スタックトレース内のフレーム番号 (空の場合は -1) JVMPI_GC_ROOT_NATIVE_STACK
(ネイティブスタック)
jobjectIDオブジェクト JNIEnv *スレッド JVMPI_GC_ROOT_STICKY_CLASS
(システムクラス)
jobjectIDクラスオブジェクト JVMPI_GC_ROOT_THREAD_BLOCK
(スレッドブロックからの参照)
jobjectIDスレッドオブジェクト JNIEnv *スレッド JVMPI_GC_ROOT_MONITOR_USED
(入られているモニター)
jobjectIDオブジェクト JVMPI_GC_CLASS_DUMP
(クラスオブジェクトのダンプ)
jobjectIDクラス jobjectIDスーパー jobjectIDクラスローダ jobjectID署名者 jobjectID保護ドメイン jobjectIDクラス名 ( Stringオブジェクト。nullのこともある)void *予約済み u4インスタンスのサイズ (バイト単位) [jobjectID]*インタフェース u2定数プールのサイズ [u2,定数プールのインデックス ty,型 vl]*値 [vl]*static フィールドの値 JVMPI_GC_INSTANCE_DUMP
(通常のオブジェクトのダンプ)
jobjectIDオブジェクト jobjectIDクラス u4あとに続くバイト数 [vl]*インスタンスフィールドの値 (クラス、スーパー、スーパーのスーパー... の順) JVMPI_GC_OBJ_ARRAY_DUMP
(オブジェクト配列のダンプ)
jobjectID配列オブジェクト u4要素の数 jobjectID要素のクラス ID (JDK では nullのこともある)[jobjectID]*要素 JVMPI_GC_PRIM_ARRAY_DUMP
(プリミティブ配列のダンプ)
jobjectID配列オブジェクト u4要素の数 ty要素の型 [vl]*要素
JVMPI_GC_CLASS_DUMP
JVMPI_GC_INSTANCE_DUMP
JVMPI_GC_OBJ_ARRAY_DUMP
JVMPI_GC_PRIM_ARRAY_DUMP
JVMPI_DUMP_LEVEL_2 と同じで、オブジェクトインスタンスダンプのプリミティブフィールド、クラスダンプのプリミティブ static フィールド、およびプリミティブ配列要素の値がすべて含まれます。
レコード型 レコードデータ JVMPI_MONITOR_JAVA
(Java モニター)
jobjectIDオブジェクト ID JNIEnv *所有するスレッド u4エントリカウント u4入るのを待っているスレッド数 [JNIEnv *]*入るのを待っているスレッド u4通知を待っているスレッド数 [JNIEnv *]*通知を待っているスレッド JVMPI_MONITOR_RAW
(raw モニター)
char*raw モニター名 JVMPI_RawMonitorraw モニター ID JNIEnv *所有するスレッド u4エントリカウント u4入るのを待っているスレッド数 [JNIEnv *]*入るのを待っているスレッド u4通知を待っているスレッド数 [JNIEnv *]*通知を待っているスレッド
jobjectID
JVMPI_CallFrame
JVMPI_CallTrace
JVMPI_Field
JVMPI_HeapDumpArg
JVMPI_Lineno
JVMPI_Method
JVMPI_RawMonitor
文字は、UTF-8 エンコーディングを使って符号化されます。Java 仮想マシンの仕様を参照してください。
jobjectIDオブジェクト ID を表す隠されたポインタ
struct _jobjectID; typedef struct _jobjectID * jobjectID;
JVMPI_CallFrame実行されているメソッド
typedef struct {
jint lineno;
jmethodID method_id;
} JVMPI_CallFrame;
フィールド:
line number- ソースファイル内の行番号 method_id- 実行されているメソッド
JVMPI_CallTrace
typedef struct {
JNIEnv *env_id;
jint num_frames;
JVMPI_CallFrame *frames;
} JVMPI_CallTrace;
フィールド:
env_id- このトレースを実行したスレッドの ID num_frames- トレース内のフレーム数 frames- このトレースを構成する JVMPI_CallFrame。呼び出される側のあとに、呼び出し側が続く
JVMPI_Field
typedef struct {
char *field_name;
char *field_signature;
} JVMPI_Field;
フィールド:
field_name- フィールドの名前 field_signature- フィールドのシグニチャー
JVMPI_HeapDumpArg要求するヒープダンプについての補足情報
typedef struct {
jint heap_dump_level;
} JVMPI_HeapDumpArg;
フィールド:
heap_dump_level- ヒープのダンプ情報のレベル。次の値を指定できる
JVMPI_DUMP_LEVEL_0JVMPI_DUMP_LEVEL_1JVMPI_DUMP_LEVEL_2
JVMPI_Lineno
typedef struct {
jint offset;
jint lineno;
} JVMPI_Lineno;
フィールド:
offset- メソッドの先頭からのオフセット lineno- ソースファイルの先頭からの行番号
JVMPI_Method
typedef struct {
char *method_name;
char *method_signature;
jint start_lineno;
jint end_lineno;
jmethodID method_id;
} JVMPI_Method;
フィールド:
method_name- メソッドの名前 method_signature- メソッドのシグニチャー start_lineno- ソースファイルの開始行番号 end_lineno- ソースファイルの終了行番号 method_id- このメソッドに与えられた ID
JVMPI_RawMonitorraw モニターを表す隠されたポインタ
struct _JVMPI_RawMonitor; typedef struct _JVMPI_RawMonitor * JVMPI_RawMonitor;
JVMPI_EVENT_METHOD_ENTRYJVMPI_EVENT_METHOD_ENTRY2JVMPI_EVENT_METHOD_EXITJVMPI_EVENT_COMPILED_METHOD_LOADJVMPI_EVENT_COMPILED_METHOD_UNLOAD
JVMPI_GC_CLASS_DUMP ダンプレコードに、クラス名文字列オブジェクトを表す jobjectID が出力されるようになった。このフィールドは、JDK のバージョン 1.2 で予約されていた。このフィールドはクラス名キャッシュとして VM によって保守されるので、指定されたクラスについて設定しなくてもよい
JVMPI_EVENT_OBJECT_ALLOC イベントが、未知要素のクラス ID を設定して発行される (つまり、class_id フィールドが常に null)
SuspendThread または SuspendThreadList は、GC が無効な状態で呼び出す必要がある。GC は、すべてのスレッドが再開されるまで無効でなければならない
JNIEnv インタフェースポインタを参照するいくつかのほかのイベントのあとに着信することがある
JVMPI_EVENT_ARENA_NEW および JVMPI_EVENT_ARENA_DELETE イベントは、発行されることがない。ほかのイベント内の領域 ID は、常に 1 に設定される
jobjectID2jobject および jobject2jobjectID が追加された。これらの関数の用途はごく限られている (注を参照)
* この Web サイトで使用されている用語「Java 仮想マシン」または「JVM」は、Java プラットフォーム用の仮想マシンを表します。