Java Virtual Machine Profiler Interface (JVMPI)
ドラフト版
最終更新日: 1998 年 10 月 16 日 (金) 12:42:48 PDT
このドキュメントでは、JDK 1.2 の Java Virtual Machine Profiler Interface (JVMPI) について説明します。このインタフェースは、Sun の Java Virtual Machine の実装とともに機能するプロファイラを開発する、ツールベンダーのために提供されています。
注: このインタフェースは、JDK 1.2 の最終リリースでの試験的な機能です。JVMPI は、現時点では標準のプロファイリング用インタフェースではありません。このドキュメントは、Java Virtual Machine でのプロファイリング用フックを至急必要としているツールベンダーを対象として提供されています。JVMPI は、カスタマおよびツールベンダーからのフィードバックに基づいて今後も改良されます。コメントは、jvmpi@eng.sun.com までお寄せください。
目次
1. 概要
JVMPI は、Java Virtual Machine とプロセス中のプロファイラエージェントとの間の双方向の関数呼び出しインタフェースです。Virtual Machine は、プロファイラエージェントに、ヒープ割り当て、スレッドの開始などに対応するさまざまなイベントを通知します。一方、プロファイラエージェントは、JVMPI を使ってより多くの情報を制御、および要求します。たとえば、プロファイラエージェントは、プロファイラフロントエンドの必要に基づき、特定のイベント通知をオンまたはオフすることができます。
プロファイラフロントエンドは、必ずしもプロファイラエージェントと同じプロセスで実行される必要はありません。プロファイラフロントエンドが、同じマシン上の別のプロセスにあったり、ネットワークで接続されたリモートマシン上のプロセスにある場合もあります。JVMPI は、標準のワイヤプロトコルを指定しません。ツールベンダーは、ほかのプロファイラフロントエンドの必要に応じてワイヤプロトコルを設計できます。
JVMPI を基にしたプロファイリングツールを使用することにより、多量のメモリが割り当てられている場所、CPU に負荷のかかるホットスポット、不必要なオブジェクトの保存、モニターの競合など、パフォーマンス全般の分析に役立つ多くの情報が取得できます。
JVMPI によって、部分的なプロファイリングもサポートされています。ユーザは、JVM が動作する特定の期間を指定してアプリケーションのプロファイリングを行なったり、特定の種類のプロファイリング情報を選んで取得することもできます。
現在のバージョンの JVMPI では、1 つの JVM につき 1 つのエージェントだけをサポートできます。
1.1. 初期設定
ユーザは、コマンド行オプションを使って Java Virtual Machine に、プロファイラエージェントの名前およびプロファイラエージェントに対するオプションを指定できます。たとえば、ユーザが次のように指定したとします。
java -Xrunmyprofiler:heapdump=on,file=log.txt ToBeProfiledClass
VM は、Java の次のライブラリディレクトリで myprofiler というプロファイラエージェントライブラリを探します。
-
Win32 の場合、$JAVA_HOME¥bin¥myprofiler.dll
-
SPARC/Solaris の場合、$JAVA_HOME/lib/sparc/libmyprofiler.so
Java ライブラリディレクトリでライブラリが見つからない場合は、それぞれのプラットフォームの通常のライブラリ検索方法に従って、ライブラリの検索が続けられます。
- Win32 では、VM はカレントディレクトリ、Windows システムディレクトリ、および
PATH
環境変数に指定されたディレクトリを検索する
- Solaris では、VM は
LD_LIBRARY_PATH
に指定されたディレクトリを検索する
VM はプロファイラエージェントライブラリをロードし、エントリポイントを探します。
jint JNICALL JVM_OnLoad(JavaVM *jvm, char *options, void *reserved);
VM は、JavaVM
インスタンスへのポインタを第 1 引数、文字列 "heapdump=on,file=log.txt" を第 2 引数として渡して、JVM_OnLoad 関数を呼び出します。JVM_OnLoad
への第 3 引数は予約されており、NULL
に設定されます。
成功した場合は、JVM_OnLoad
関数は JNI_OK
を返します。なんらかの理由で JVM_OnLoad
関数の実行が失敗した場合は、JNI_ERR
を返します。
1.2. 関数呼び出しインタフェース
プロファイラエージェントは、JavaVM
ポインタ上で GetEnv
の呼び出しを発行することによって、関数呼び出しインタフェースを取得できます。たとえば、次のコードは、JDK 1.2 で実装された 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);
} 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 を呼び出すことにより、イベントを送信します。サポートするイベントは次のとおりです。
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 Virtual Maichine 内のエンティティはさまざまな種類の ID で表されます。スレッド、クラス、メソッド、オブジェクト、ヒープ領域、および JNI グローバル参照には、すべて一意の ID があります。
各 ID には、定義イベントおよび定義取り消しイベントがあります。定義イベントは、ID に関連した情報を提供します。たとえば、スレッド ID の定義イベントには、ほかのエントリとともにそのスレッドの名前が含められます。
ID は、定義取り消しイベントが着信するまで有効です。定義取り消しイベントは ID を無効にし、その後その ID の値は、別の種類の ID として再利用が可能になります。たとえば、スレッド ID の値を、スレッドの終了後はメソッド ID として定義することが可能です。
-
定義イベントがプロファイラの初期化中に有効である場合、あるエンティティがほかの 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 を無効にする必要があります。無効にしないと、エージェントコードで jobjectID
が操作されているときに、GC によって jobjectID
が無効になる可能性があります。プロファイラエージェントは、jobjectID
の引数をとるか、または jobjectID
の結果を返す JVMPI 関数を呼び出す場合には、必ず GC を無効にする必要があります。ただし、GC がすでに無効になっているイベントハンドラ内に関数呼び出しがある場合、プロファイラエージェントは明示的に再度 GC を無効にする必要はありません。
スレッドは、JNIEnv
インタフェースポインタか、または対応する java.lang.Thread
オブジェクトのオブジェクト ID によって識別が可能です。JNIEnv
ポインタは、スレッド開始イベントからスレッド終了イベントまで有効で、スレッドが有効な間は一定です。これに対し、java.lang.Thread
オブジェクト ID は、スレッド終了後もガベージコレクトされるまで有効なままである可能性があります。プロファイラエージェントは、GetThreadObject
関数を呼び出すことによって、JNIEnv
ポインタを、対応するスレッドオブジェクト ID に変換できます。
1.5. スレッドとロックの問題
JVMPI は、Java Virtual Machine と同じプロセス内で実行されているプロファイラエージェントによって使用されます。エージェントを記述するプログラマは、データの破壊とデッドロックを防ぐために、スレッドとロックの扱いに注意する必要があります。
イベントは、生成されたスレッドと同じスレッド内で送信されます。たとえば、クラスロードイベントは、クラスがロードされるスレッドと同じスレッド内で送信されます。複数のイベントが同時に異なるスレッドに着信する可能性があります。このため、複数のスレッドが同じデータ構造体を同時に更新することによるデータの破壊を防ぐために、エージェントプログラムは、必要な同期を提供しなければなりません。
頻繁に発生する特定のイベント (メソッドに入る、メソッドから出るなど) の同期が、プログラムの実行に過大なオーバーヘッドを課す場合があります。このため、エージェントは、全体をロックせずに、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 Virtual Machine にロードされる際のプロセスは、GC が有効なマルチスレッドモード、GC が無効なマルチスレッドモード、およびスレッド中断モードのどれかで実行されます。モードによって発行される JVMPI イベントが異なります。特定の JVMPI 関数により、あるモードのプロセスが別のモードに変わることもあります。
デッドロックを避けるため、プロファイラエージェントは、次のガイドラインに従う必要があります。
-
GC が有効なマルチスレッドモードでは、エージェントコードで自由にロックの獲得および JVMPI の呼び出しを行うことができるが、通常のデッドロック回避の規則が当然適用される。異なるスレッドが、同じロックのセットに異なる順序で入ってはならない
- GC が無効な場合、エージェントプログラムは、新しい Java オブジェクトの作成を必要とする JVMPI 関数や、ガベージコレクトが実行される可能性のある JVMPI 関数を呼び出してはならない。現在このような関数には、
CreateSystemThread
や RunGC
などがある。さらに、プログラマは、GC を無効にすることによってスレッド間に暗黙的にロックの依存性が発生することに注意する必要がある。GC が無効になると、現在のスレッドが特定のロックを安全に獲得できない場合がある。たとえば、あるスレッドが GC を無効にしてロックを獲得しようとした場合に、別のスレッドがすでにそのロックを獲得している場合、GC が呼び出されると、デッドロックが発生する
-
スレッド中断モードでは、中断された 1 つまたは複数のスレッドがある。この場合、エージェントプログラムは、現在のスレッドがブロックする可能性のある操作を実行してはならない。このような操作には、たとえば、標準 C ライブラリによって提供される
malloc
や fprintf
などがある。これらの関数は、一般的に内部の C ライブラリのロックを獲得するが、中断されたスレッドの 1 つによってそのロックが保持されている可能性がある
2. インタフェース関数
jint (*CreateSystemThread)(char *name, jint priority, void (*f)(void *));
- プロファイラエージェントによって呼び出され、Java Virtual Machine 内にデーモンスレッドを作成します。
プロファイラエージェントによるこの関数の呼び出しを、JVM が JVMPI_EVENT_INIT_DONE
を通知したあとで、システムが GC が有効なマルチスレッドモードのときに限定すると安全です。
引数:
name
| - スレッド名
|
priority
| - スレッドの優先度。次の値の場合がある
|
|
JVMPI_NORMAL_PRIORITY |
JVMPI_MAXIMUM_PRIORITY |
JVMPI_MINIMUM_PRIORITY |
|
f
| - スレッドによって実行される関数
|
戻り値:
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 を無効にする必要があります。
引数:
戻り値:
void * (*GetThreadLocalStorage)(JNIEnv *env_id);
- JVMPI スレッドローカルの記憶領域の値を取得するために、プロファイラによって呼び出されます。JVMPI は、エージェントに、スレッドごとのプロファイリング情報の記録に使用できるポインタサイズのスレッドローカルな記憶領域を提供します。
引数:
戻り値:
jobjectID (*GetThreadObject)(JNIEnv *env);
JNIEnv
ポインタに対応するスレッドオブジェクト ID を取得するために、プロファイラエージェントによって呼び出されます。
プロファイラは、この関数を呼び出す前に GC を無効にする必要があります。
引数:
戻り値:
jint (*GetThreadStatus)(JNIEnv *env);
- スレッドの状態を取得するために、プロファイラエージェントによって呼び出されます。
JVMPI 関数 SuspendThread
および ResumeThread
は、GetThreadStatus
が返す状態には影響しません。JVMPI によって中断されたスレッドの状態は変更されずに残り、中断時の状態が返されます。
引数:
戻り値:
JVMPI_THREAD_RUNNABLE
| - スレッドは実行可能
|
JVMPI_THREAD_MONITOR_WAIT
| - スレッドはモニターを待機している
|
JVMPI_THREAD_CONDVAR_WAIT
| - スレッドは条件変数を待機している
|
スレッドが (java.lang.Thread.suspend
によって) 中断されるか、または上の状態のどれかによって割り込まれると、JVMPI_THREAD_SUSPENDED
または JVMPI_THREAD_INTERRUPTED
ビットが設定されます。
void (*NotifyEvent)(JVMPI_Event *event);
- VM によって呼び出され、プロファイリングエージェントにイベントを送信します。プロファイラエージェントは、
EnableEvent
を呼び出すことによって必要な型のイベントを登録するか、または RequestEvent
を呼び出すことによって特定の型のイベントを要求します。
EnableEvent
によってイベントが有効になる場合は、イベントを生成するスレッドでイベントが送信されます。RequestEvent
によってイベントが要求される場合は、イベントを要求するスレッドでイベントが送信されます。複数のスレッドが、複数のイベントを同時に送信することが可能です。
イベント特有の情報に jobjectID
が含まれている場合は、この関数は、GC が無効な状態で呼び出されます。GC は、関数の復帰後に有効になります。
JVMPI_Event
構造体およびイベント特有の情報に割り当てられた領域は、この関数の復帰後に JVM によって解放されます。プロファイラエージェントは、残しておく必要のあるデータを内部バッファにコピーする必要があります。
引数:
event
| - VM からプロファイリングエージェントに送信される JVMPI イベント
|
void (*ProfilerExit)(jint err_code);
- プロファイラエージェントによって呼び出され、エラーコードに
err_code
を設定した状態でプロファイラが終了する旨を VM に通知します。この関数により、VM も同じ err_code
で終了します。
引数:
JVMPI_RawMonitor (*RawMonitorCreate)(char *lock_name);
- プロファイラによって呼び出され、raw モニターを作成します。
raw モニターは、Java モニターと似ていますが、Java オブジェクトに関連付けられていない点が異なります。
プロファイラエージェントが、スレッド中断モードでこの関数を呼び出すことは安全ではありません。これは、この関数が malloc
などの任意のシステム関数を呼び出して、内部システムライブラリのロックをブロックする可能性があるからです。
raw モニターがアンダースコア (_
) で始まる名前で作成された場合は、そのモニターのコンテンションイベントはプロファイラエージェントに送信されません。
引数:
戻り値:
void (*RawMonitorDestroy)(JVMPI_RawMonitor lock_id);
- プロファイラエージェントによって呼び出され、raw モニターを破棄してそのモニターに関連付けられているすべてのシステムリソースを解放します。
raw モニターは、Java モニターと似ていますが、Java オブジェクトに関連付けられていない点が異なります。
プロファイラエージェントが、スレッド中断モードでこの関数を呼び出すことは安全ではありません。これは、この関数が free
などの任意のシステム関数を呼び出して、内部システムライブラリのロックをブロックする可能性があるからです。
引数:
void (*RawMonitorEnter)(JVMPI_RawMonitor lock_id);
- プロファイラエージェントによって呼び出され、raw モニターに入ります。
raw モニターは、Java モニターと似ていますが、Java オブジェクトに関連付けられていない点が異なります。
プロファイラエージェントが、スレッド中断モードでこの関数を呼び出すことは安全ではありません。これは、現在のスレッドが、すでに中断されているスレッドの 1 つによって獲得されている raw モニターをブロックする可能性があるからです。
引数:
void (*RawMonitorExit)(JVMPI_RawMonitor lock_id);
- プロファイラエージェントによって呼び出され、raw モニターから出ます。
raw モニターは、Java モニターと似ていますが、Java オブジェクトに関連付けられていない点が異なります。
引数:
void (*RawMonitorNotifyAll)(JVMPI_RawMonitor lock_id);
- プロファイラによって呼び出され、raw モニターを待機しているすべてのスレッドに通知を送ります。
raw モニターは、Java モニターと似ていますが、Java オブジェクトに関連付けられていない点が異なります。
引数:
void (*RawMonitorWait)(JVMPI_RawMonitor lock_id, jlong ms);
- プロファイラエージェントによって呼び出され、指定したタイムアウトまでの期間 raw モニターを待機します。タイムアウト期間に 0 を指定すると、スレッドは永遠に待機します。
raw モニターは、Java モニターと似ていますが、Java オブジェクトに関連付けられていない点が異なります。
引数:
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 を解釈処理する必要があるのに、対応する定義イベントがすでに無効になっている場合に便利です。
このため、プロファイラエージェントは 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
関数では再開できません。
引数:
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 1.2 の実装では、この関数は、GC が無効な状態で呼び出す必要があります。GC は、すべてのスレッドが再開されるまで無効でなければなりません。
引数:
jboolean (*ThreadHasRun)(JNIEnv *env);
- プロファイラエージェントによって呼び出され、特定の
JNIEnv
ポインタによって識別されたスレッドが、SuspendThread
によって最後に中断されたとき以降に CPU 時間を消費したかどうかを特定します。この関数は、スレッドが ResumeThread によって再開されたとき、および SuspendThread
関数によって再度中断されたときに呼び出される必要があります。
引数:
戻り値:
JNI_TRUE
| - スレッド実行の機会があった
|
JNI_FALSE
| - スレッド実行の機会がなかった
|
3. イベント
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;
内容:
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;
内容:
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 1.2 では、Win32 上では Ctrl+Break キー、Solaris 上では Ctrl+¥ キーを押すと、VM がこのイベントをプロファイラエージェントに送信します。
イベント特有の情報はありません。
JVMPI_EVENT_DATA_RESET_REQUEST
-
VM によって送信され、プロファイラエージェントにデータをリセットするように要求します。これは単に示唆するだけであり、プロファイラエージェントはこのイベントに反応する必要はありません。これは、ユーザからのコマンド行シグナルを処理する場合に便利です。たとえば、JDK 1.2 では、Win32 上では 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;
内容:
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;
内容:
JVMPI_EVENT_JVM_INIT_DONE
-
初期化時に VM によって送信されます。
CreateSystemThread
を呼び出して安全なのは、このイベントが通知されたあとだけです。
イベント特有データはありません。
JVMPI_EVENT_JVM_SHUT_DOWN
-
VM がシャットダウンしているときに VM によって送信されます。通常、プロファイラは、プロファイリングデータを保存することによって対応します。
イベント特有データはありません。
JVMPI_EVENT_METHOD_ENTRY
-
メソッドに入るときに送信されます。
JVMPI_EVENT_METHOD_ENTRY2
と比較すると、このイベントはメソッドの呼び出し対象であるオブジェクトの jobjectID
を送信しません。
struct {
jmethodID method_id;
} method;
内容:
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;
内容:
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 は永久に待機することを示す)
|
JVMPI_EVENT_MONITOR_WAITED
- スレッドがオブジェクトに対する待機を終了するときに送信されます。
このイベントは、GC が無効な状態で発行されます。GC は、NotifyEvent
から復帰したあとに、再度有効になります。
struct {
jobjectID object;
jlong timeout;
} monitor_wait;
内容:
object
| - 現在のスレッドの待機対象のオブジェクト ID
|
| (NULL は、スレッドが Thread.sleep であることを示す)
|
timeout
| - スレッドが待機した時間 (単位はミリ秒)
|
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;
内容:
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 として使います。
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 *) |
|
4.2 ヒープのダンプ形式
ヒープのダンプ形式は、要求される情報のレベルによって異なります。
JVMPI_DUMP_LEVEL_0:
ダンプは、次の形式の一連のレコードで構成されます。
ty | オブジェクトの型
|
jobjectID | オブジェクト
|
JVMPI_DUMP_LEVEL_1:
ダンプ形式は、JVMPI_DUMP_LEVEL_2
のダンプ形式と同じですが、オブジェクトインスタンスダンプのプリミティブフィールド、クラスダンプのプリミティブ static フィールド、およびプリミティブ配列要素の各値が除外されている点だけが異なります。
JVMPI_DUMP_LEVEL_2:
ダンプは、一連のレコードで構成され、各レコードには、8 ビットのレコード型のあとに各レコード型固有の形式のデータが含まれています。
レコード型 | レコードデータ
|
JVMPI_GC_ROOT_UNKNOWN (未知のルート)
|
|
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 (システムクラス)
|
|
JVMPI_GC_ROOT_THREAD_BLOCK (スレッドブロックからの参照)
|
jobjectID | スレッドオブジェクト
| JNIEnv * | スレッド
|
|
JVMPI_GC_ROOT_MONITOR_USED (入るモニター)
|
|
JVMPI_GC_CLASS_DUMP (クラスオブジェクトのダンプ)
|
jobjectID | クラス
| jobjectID | スーパー
| jobjectID | クラスローダ
| jobjectID | 署名者
| jobjectID | 保護ドメイン
| void * | 予約
| 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 1.2 では NULL の可能性もある)
| [jobjectID]* | 要素
|
|
JVMPI_GC_PRIM_ARRAY_DUMP (プリミティブ配列のダンプ)
|
jobjectID | 配列オブジェクト
| u4 | 要素の数
| ty | 要素の型
| [vl]* | 要素
|
|
4.3 オブジェクトのダンプ形式
ダンプのバッファは、8 ビットのレコード型のあとにそのレコード型固有のデータが続くような、単一のレコードから構成されます。レコード型は次のどれかになります。
JVMPI_GC_CLASS_DUMP
JVMPI_GC_INSTANCE_DUMP
JVMPI_GC_OBJ_ARRAY_DUMP
JVMPI_GC_PRIM_ARRAY_DUMP
各レコード型のデータの形式は、上記の「ヒープのダンプ形式」の項で説明した形式と同じです。情報のレベルは、JVMPI_DUMP_LEVEL_2
と同じで、オブジェクトインスタンスダンプのプリミティブフィールド、クラスダンプのプリミティブ static フィールド、およびプリミティブ配列要素の各値が含まれます。
4.4 モニターのダンプ形式
ダンプは、一連のレコードで構成され、各レコードには、8 ビットのレコード型のあとに各レコード型固有の形式のデータが含まれています。
レコード型 | レコードデータ
|
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 *]* | 通知を待っているスレッド
|
|
5. データ型
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;
6. JDK 1.2 での実装の制限について
- オブジェクト配列の
JVMPI_EVENT_OBJECT_ALLOC
イベントは、未知要素のクラス ID (たとえば class_id
フィールドが常に NULL
) を使って発行される
- Win 32 上では、JIT コンパイラは、現時点では次のイベントをサポートしていない
JVMPI_EVENT_METHOD_ENTRY
JVMPI_EVENT_METHOD_ENTRY2
JVMPI_EVENT_METHOD_EXIT
JVMPI_EVENT_COMPILED_METHOD_LOAD
JVMPI_EVENT_COMPILED_METHOD_UNLOAD
-
SuspendThread
は、GC が無効な状態で呼び出す必要がある。GC は、すべてのスレッドが再開されるまで無効でなければならない
- メインスレッド (VM が最初に作成したスレッド) に対するスレッドの開始イベントは、
JNIEnv
インタフェースポインタを参照するいくつかのほかのイベントのあとに着信する
JVMPI_EVENT_ARENA_NEW
および JVMPI_EVENT_ARENA_DELETE
イベントは、発行されない。ほかのイベント内の領域 ID は、常に 1 に設定される