JavaTM Virtual Machine Profiler Interface (JVMPI)


このドキュメントでは、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 テクノロジに特有の注意事項がある場合は、その旨記載されます。

目次


1. 概要

JVMPI は、Java 仮想マシンとインプロセスのプロファイラエージェントとの間の、双方向の関数呼び出しインタフェースです。仮想マシンは、ヒープの割り当てやスレッドの開始などに対応するさまざまなイベントを、プロファイラエージェントに通知します。他方、プロファイラエージェントは、JVMPI を使って多くの情報を得るために制御や要求を発行します。たとえば、プロファイラエージェントは、プロファイラのフロントエンドの必要に基づいて、特定のイベント通知をオンまたはオフにすることができます。

詳細な説明を参照[D]

プロファイラフロントエンドは、必ずしもプロファイラエージェントと同じプロセスで実行される必要はありません。プロファイラフロントエンドは、同じマシン上の別のプロセスに置いたり、ネットワーク経由で接続されたリモートマシン上のプロセスに置いたりできます。JVMPI は、標準のワイヤプロトコルを指定しません。ツールベンダーは、個々のプロファイラフロントエンドの必要に応じて、ワイヤプロトコルを設計できます。

JVMPI を基にしたプロファイリングツールを使用することにより、多量のメモリが割り当てられている場所、CPU に負荷のかかるホットスポット、不必要なオブジェクトの保持、モニターの競合など、パフォーマンス全般の分析に役立つ多くの情報を取得できます。

JVMPI では、部分的なプロファイリングもサポートされています。つまり、ユーザは、仮想マシンの動作中の特定期間を指定してアプリケーションのプロファイリングを実行したり、特定の種類のプロファイリング情報を選んで取得したりできます。

現在のバージョンの JVMPI では、1 つの仮想マシンにつき 1 つのエージェントだけをサポートできます。

1.1. 初期設定

ユーザは、Java 仮想マシンに対するコマンド行オプションを使って、プロファイラエージェントの名前と、プロファイラエージェントに対するオプションを指定できます。たとえば、ユーザが次のように指定したとします。
     java -Xrunmyprofiler:heapdump=on,file=log.txt ToBeProfiledClass
VM は、Java の次のライブラリディレクトリから myprofiler というプロファイラエージェントライブラリを探します。 Java ライブラリディレクトリにライブラリが見つからない場合は、それぞれのプラットフォームの通常のライブラリ検索方法に従って、ライブラリの検索が続けられます。 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 に実装されたバージョンの 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_Interfaceversion フィールドは、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 仮想マシン内のエンティティが、さまざまな種類の 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 が無効にされた場合、そのオブジェクトはガベージコレクトされたと言います。

通常、プロファイラエージェントは、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 関数は、プロセスを、あるモードから別のモードに変更します。

デッドロックを避けるため、プロファイラエージェントは、次のガイドラインに従う必要があります。

1.6. プロファイラエージェントとフロントエンドの間のデータ通信

JVMPI は、プロファイラエージェントが仮想マシンと通信するための下位レベルの機構を提供しています。この機構の目的は、フロントエンドの要件に応じてデータを提示する柔軟性をプロファイラエージェントに与え、仮想マシン側で実行される処理を最小限に抑えることです。このため、JVMPI は、プロファイリングエージェントとフロントエンドの間のワイヤプロトコルを指定していません。その代わりに、ツールベンダーが、各フロントエンドの要件に応じて独自のプロファイリングエージェントを設計します。

プロファイラエージェントとフロントエンドを異なるマシンに配置できるようにするには、ワイヤプロトコルを設計するときに、次の点について考慮する必要があります。

たとえば、Java 2 SDK 1.4.2 以前に付属している HPROF プロファイラエージェントでは、すべての ID のサイズが最初のレコードとして送信され、整数および浮動小数点のデータには標準ネットワークのバイト順が使用されます。

2. インタフェース関数

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_typeJVMPI_EVENT_HEAP_DUMPJVMPI_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_typeJVMPI_EVENT_HEAP_DUMPJVMPI_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.suspendSuspendThread、または 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_DUMPJVMPI_EVENT_MONITOR_DUMPJVMPI_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_typeJVMPI_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_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_datanew_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 のルートが存在するスタックトレース

beginend の間に置かれるヒープダンプの形式は、要求された情報のレベルによって異なります。この形式の詳細については、「ダンプの形式」の項を参照してください。

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_arrayJVMPI_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_Eventenv_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 *)

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
(未知のルート)
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]* 要素

4.3. オブジェクトのダンプ形式

ダンプのバッファは、8 ビットのレコード型と、そのあとにレコード型固有のデータが続くという形式のレコードで構成されます。レコード型は、次のどれかになります。 各レコード型のデータの形式は、「ヒープのダンプ形式」で説明した形式と同じです。情報のレベルは、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. データ型

文字は、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;

6. Java 2 SDK, v1.2 からの変更について

6.1. 新機能

6.2. 制限

6.3 VERSION_1_1 での変更点

6.4 VERSION_1_2 での変更点

7. JMVPI HPROF プロファイラエージェント

JVMPI は J2SE 5.0 で非推奨となったため、HPROF は新しい Java Virtual Machine Tools Interface を使用するように書き換えられました。JVMPI ベースの HPROF エージェントについては、「Java 2 SDK 1.4.2 JVMPI HPROF Agent」を参照してください。

8. コード例

8.1. 簡単なクラスロードトレーサ

jvmpi_example.zip ファイルには、Java プログラムによってロードされるすべてのクラスをトレースできる、サンプルプロファイラエージェントが含まれています。Solaris 版と Microsoft Windows 版の 2 つが含まれています。

* この Web サイトで使用されている用語「Java 仮想マシン」または「JVM」は、Java プラットフォーム用の仮想マシンを表します。


最終更新日:2004 年 5 月 7 日 (金) 17:04:15 PDT