このドキュメントでは、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 テクノロジに特有の注意事項がある場合は、その旨記載されます。
プロファイラフロントエンドは、必ずしもプロファイラエージェントと同じプロセスで実行される必要はありません。プロファイラフロントエンドは、同じマシン上の別のプロセスに置いたり、ネットワーク経由で接続されたリモートマシン上のプロセスに置いたりできます。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_PRIORITY
JVMPI_MAXIMUM_PRIORITY
JVMPI_MINIMUM_PRIORITY
f
- スレッドによって実行される関数 戻り値:
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_0
JVMPI_DUMP_LEVEL_1
JVMPI_DUMP_LEVEL_2
null 値が渡された場合、ダンプレベルは
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_BOOLEAN
boolean 型の配列 JVMPI_BYTE
byte 型の配列 JVMPI_CHAR
char 型の配列 JVMPI_SHORT
short 型の配列 JVMPI_INT
int 型の配列 JVMPI_LONG
long 型の配列 JVMPI_FLOAT
float 型の配列 JVMPI_DOUBLE
double 型の配列 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_BOOLEAN
boolean 型の配列 JVMPI_BYTE
byte 型の配列 JVMPI_CHAR
char 型の配列 JVMPI_SHORT
short 型の配列 JVMPI_INT
int 型の配列 JVMPI_LONG
long 型の配列 JVMPI_FLOAT
float 型の配列 JVMPI_DOUBLE
double 型の配列 vl:
値。正確なサイズは値の型によって異なる
boolean、byte
u1 short、char
u2 int、float
u4 long、double
u8 JNIEnv *
、jobjectID
、およびJVMPI_RawMonitor
sizeof(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
オブジェクト jobject
JNI グローバル参照 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_RawMonitor
raw モニター 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_0
JVMPI_DUMP_LEVEL_1
JVMPI_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_RawMonitor
raw モニターを表す隠されたポインタ
struct _JVMPI_RawMonitor; typedef struct _JVMPI_RawMonitor * JVMPI_RawMonitor;
JVMPI_EVENT_METHOD_ENTRY
JVMPI_EVENT_METHOD_ENTRY2
JVMPI_EVENT_METHOD_EXIT
JVMPI_EVENT_COMPILED_METHOD_LOAD
JVMPI_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 プラットフォーム用の仮想マシンを表します。