天天看点

Java 虚拟机监视程序接口 (JVMPI)【转载】

Java 虚拟机监视程序接口 (JVMPI)

--------------------------------------------------------------------------------

本文档介绍了 JDK 1.2 中的 Java 虚拟机监视程序接口(JVMPI)。它适合于有意用 Sun 的 Java 虚拟机实现来开发监视程序的工具厂商。 

注意:该接口是 JDK 1.2 最终版中的试验性功能。JVMPI 尚未成为标准的监视程序接口。考虑到那些在 Java 虚拟机中急需监视程序钩子的工具厂商,我们编写了本文档。JVMPI 将根据客户和工具厂商的意见而不断改进。如有意见,请发送到:[email protected]. 

目录
概述 
启动 
函数调用接口 
事件通知 
JVMPI 标识符 
线程和锁定问题 
监视程序代理和前端间的数据通信 
接口函数 
CreateSystemThread 
DisableEvent 
DisableGC 
EnableEvent 
EnableGC 
GetCallTrace 
GetCurrentThreadCpuTime 
GetMethodClass 
GetThreadLocalStorage 
GetThreadObject 
GetThreadStatus 
NotifyEvent 
ProfilerExit 
RawMonitorCreate 
RawMonitorDestroy 
RawMonitorEnter 
RawMonitorExit 
RawMonitorNotifyAll 
RawMonitorWait 
RequestEvent 
ResumeThread 
RunGC 
SetThreadLocalStorage 
SuspendThread 
ThreadHasRun 
事件 
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 
转储格式 
转储格式描述中使用的大小和类型 
堆转储格式 
对象转储格式 
监控器转储格式 
数据类型 
jobjectID 
JVMPI_CallFrame 
JVMPI_CallTrace 
JVMPI_Field 
JVMPI_HeapDumpArg 
JVMPI_Lineno 
JVMPI_Method 
JVMPI_RawMonitor 
有关 JDK1.2 实现局限性的说明 

--------------------------------------------------------------------------------


1. 概述
JVMPI 是 Java 虚拟机和进程中监视程序代理之间的双向函数调用接口。一方面,虚拟机将通知不同事件的监视程序代理(例如相应的堆分配、线程启动等)。另一方面,监视程序代理也通过 JVMPI 发送控制并请求更多的信息。例如,监视程序代理能够根据监视程序前端的需要,打开/关闭特定事件通知。 

 

监视程序前端可以与监视程序代理在相同的进程中运行,也可在不同的进程中运行。它可以驻留在同一机器的不同进程中,或者通过网络驻留在远程计算机中。JVMPI 没有指定标准通信协议。工具厂商可以根据不同监视程序前端的需要而设计适当的通信协议。 

基于 JVMPI 的监视程序工具能获取多种信息,例如用于综合性能分析的堆内存分配址、CPU 使用热点、不必要的对象保持及监控器竞争。 

JVMPI 支持部分监视程序,即用户可有选择地对虚拟机正常运行时间的子集进行应用程序的监视,并可选择仅获得特定类型的监视程序信息。 

当前的 JVMPI 版本中对每个虚拟机仅支持一个代理。 

1.1. 启动
用户可以通过 Java 虚拟机的命令行选项指定监视程序代理名和监视程序代理选项。例如,假定用户指定: 

    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 中,虚拟机将搜索当前目录、 Windows 系统目录和 PATH 环境变量中的目录。 
在 Solaris 中,虚拟机将搜索 LD_LIBRARY_PATH 中的目录。 
虚拟机加载监视程序的代理库并寻找以下入口点: 

jint JNICALL JVM_OnLoad(JavaVM *jvm, char *options, void *reserved);

虚拟机调用 JVM_OnLoad 函数,将 JavaVM 实例指针作为第一个参数,然后将字符串 "heapdump=on,file=log.txt" 作为第二个参数。JVM_OnLoad 的第三个参数将被保留并设置为 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; } ... /* 使用 jvmpi_interface 中的项*/ } 

JVMPI_Interface 结构定义了监视程序代理和虚拟机之间的函数调用接口:


/* 接口函数 */
typedef struct {
    jint version;   /* JVMPI 版*/

    /* ------监视程序实现的接口------ */

    void (*NotifyEvent)(JVMPI_Event *event);

    /* ------ 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 的指针,其 version 域指示 JVMPI 版本与 GetEnv 调用中所传递的版
本号参数是兼容的。注意 version 域的值不必与 GetEnv 调用中传递的版本参数相同。


GetEnv 返回的 JVMPI_Interface 将设置除 NotifyEvent 函数之外的所有函数。监视程序代理在从 JVM_OnLoad 返回前必须设置 NotifyEvent 函数指针。 

1.3. 事件通知
虚拟机将通过用 JVMPI_Event 数据结构作为参数调用 NotifyEvent 来发送事件。它支持下列事件: 

方法进入和退出 
对象分配、移动和释放 
堆块创建和删除 
GC 启动和完成 
JNI 全局引用分配和释放 
JNI 弱全局引用分配和释放 
编译方法的加载和卸载 
线程启动和结束 
为使用机器准备类文件数据 
类加载和卸载 
竞争的 Java 监控器等待进入、已进入和退出 
竞争的原监控器等待进入、已进入和退出 
Java 监控器等待和被等待 
监控器转储 
堆转储 
对象转储 
请求转储或重置监视程序数据 
Java 虚拟机初始化和关闭 
JVMPI_Event 结构包含事件类型、当前线程的 JNIEnv 指针及其他事件特定信息。事件特定信息用特定事件结构的联合来表示。JVMPI 事件一节提供了所有事件特定结构的完整介绍。现在,我们将显示类加载和类卸载的事件特定结构。 

typedef struct {
    jint event_type;                  /* event_type  */
    JNIEnv *env_id;                   /* 事件发生地点的 env  */

    union {
        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;

        struct {
	    jobjectID class_id;       /* 类对象标识符 */
	} class_unload;

        ... /* 要获取完整的列表,参见 JVMPI 事件一节 */
    } u;
} JVMPI_Event;


1.4. JVMPI 标识符
JVMPI 将 Java 虚拟机中的实体视作不同类型的标识符。线程、类、方法、对象、堆块和 JNI 全局引用都具有唯一的标识符。 

每个标识符具有定义事件和未定义事件。定义事件提供标识符的相关信息。例如,线程标识符的定义事件包含有线程名和其他项。 

标志符在未定义事件到达之前将一直有效。未定义事件使标识符无效,其值以后可作为其它类型的标识符。例如,线程标识符值可以在线程终止后重定义为方法标识符。 

标识符 数据类型 定义事件 未定义事件 
线程标识符 JNIEnv * 线程启动 线程结束 
对象标识符 jobjectID 对象分配 对象释放、对象移动 和块删除 
类标识符 jobjectID 类加载 类卸载和对象移动 
方法标识符 jmethodID 定义类加载 定义类卸载 
块标识符 jint 新建块 删除块 
JNI 全局引用标识符 jobject 全局引用分配 全局引用释放 

假定在监视程序初始化期间启用定义事件,则应保证实体在其它 JVMPI 事件中出现前通过定义事件将实体的创建通知给监视程序代理。 

如果定义事件没有启用,监视程序代理将会接收未知标识符。这种情况下,监视程序代理可能发出 RequestEvent 调用,请求发送相应的定义事件。 

代表对象的标识符具有 jobjectID 类型。类由相应 java.lang.Class 对象的对象标识符表示。因此,类标识符也具有 jobjectID 类型。 

jobjectID 由 object alloc 事件定义,并在对象所分配的块中保持有效,直到其未定义事件发生: 

对象释放事件使对象标识符无效。 
对象移动事件是一种特殊的未定义事件。与其它指示相应实体终止的未定义事件不同,对象将仍然存在,但其标识符发生改变,并且可能被移动到新块中。 
删除块事件使所有块中保留的对象标识符无效。 
当对象释放或删除块事件使对象标识符无效时,该对象即“被作为垃圾收集”。 

一般而言,监视程序代理维持 jobjectID 及其对象标识符的内部表示之间的映像,并更新相应的 JVMPI 对象标识符映像。 

由于对象标识符在 GC 期间可以失效,所以虚拟机发出所有含有禁用 GC 的 jobjectID 项的事件。另外,监视程序代理在直接操作 jobjectID 数据类型时必须禁止 GC。否则,GC 会在代理代码中操作 jobjectID 时使它无效。监视程序代理将确保在调用接受 jobjectID 参数或者返回 jobjectID 结果的 JVMPI 函数时禁用 GC。如果函数调用处于已经禁用 GC 的事件处理程序内,则监视程序代理可不必显式地再次禁用 GC。 

线程可由 JNIEnv 接口指针或相应 java.lang.Thread 对象的对象标识符所标识。JNIEnv 指针将在线程启动和线程结束事件之间有效,并在线程生命期内保持不变。另一方面,java.lang.Thread 对象标识符在线程结束后仍能保持有效,直到它被作为垃圾收集。监视程序代理能够通过调用 GetThreadObject 函数,将 JNIEnv 指针转换为相应的线程对象标识符。 

1.5. 线程和锁定问题
JVMPI 由监视程序代理使用,后者与 Java 虚拟机在相同的进程中运行。编写代理的程序员一定要仔细处理线程和锁定问题,以防数据破坏和死锁。 

事件将被发送到它们产生的同一线程中。例如,类加载事件将发送到加载该类的线程中。多个事件可同时在不同线程中发生。因此,代理程序必须提供必要的同步,以避免由于多个线程同时更新相同的数据结构而引起数据破坏。 

某些情况下,某些频繁事件的同步(例如方法入口和方法出口)可能会给程序执行带来过多的开支。代理可利用 JVMPI 提供的局部线程存储支持来记录监视程序数据,而无需与全局锁竞争,并且将只在选定间隔将局部线程数据合并到全局监视程序中。JVMPI 可以为代理提供指针大小的局部线程存储空间。下面是一个简单的示例,演示了监视程序代理如何利用该功能。假定我们需要编写监视程序代理来计算每个线程中执行的方法数。代理将安装线程启动、方法入口和线程结束事件的事件处理程序: 

/* 线程启动事件处理程序
 * 设置局部线程方法调用计数器的存储空间
 */
void ThreadStartHandler(JNIEnv *thread_id)
{
    int *p_ctr = (int *)malloc(sizeof(int));
    CALL(SetThreadLocalStorage)(thread_id, p_ctr);
}

/* 方法进入事件处理程序
 * 增量局部线程方法调用计数器 
 */
void MethodEntryHandler(jmethodID method_id, JNIEnv *thread_id)
{
    int *p_ctr = (int *)CALL(GetThreadLocalStorage)(thread_id);
    (*p_ctr)++;
}

/* 线程结束处理程序
 * 打印执行的方法数
 */
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 
CreateSystemThread 
RunGC 
RequestEvent 函数提供监视程序代理显式要求的 JVMPI 事件。CreateSystemThread 函数导致发出对象分配和线程启动事件。RunGC 函数可生成 GC 相关事件。 

当监视程序代理加载到 Java 虚拟机中后,该进程可能成为三种模型之一:支持 GC 的多线程模式、不支持 GC 的多线程模式及挂起的线程模式。不同的 JVMPI 事件以不同的模式发布。某些 JVMPI 函数将进程从一种模式改变成另一种模式。 

为避免死锁,监视程序代理必须遵守下列指导方针: 

在支持 GC 的多线程模式中,代理模式在获得锁定和调用 JVMPI 函数方面应具有很大的自由。当然,避免死锁的正常规则仍然适用。不同的线程不能以不同的次序进入同一锁定集。 
禁用 GC 后,代理程序不能调用任何要求创建新 Java 对象或使垃圾收集器运行的 JVMPI 函数。目前,此类函数包括 CreateSystemThread 和 RunGC。另外,程序员需要知道禁用 GC 将会在线程之间创建隐含锁定依赖。当禁用 GC 时,当前的线程将不能安全获得某些锁定。举例而言,如果线程禁用 GC 并试图获得锁定,而其它线程已获得该锁定但又触发了 GC,就可能会发生死锁。 
在线程挂起模式里,一个或多个线程已经挂起。这种情况下,代理程序不能执行任何引起当前线程阻塞的操作。此类操作包括由标准 C 库提供的 malloc 和 fprintf 函数,它们通常可获得由其中某一挂起线程保持的内部 C 库锁定。 

1.6 监视程序代理和前端间的数据通信。
JVMPI 为监视程序代理提供了一种低级机制来与虚拟机进行通信,目标是为根据前端需要而展示数据的监视程序代理提供最大的灵活性,同时保持虚拟机所做的处理工作量最小。因此,JVMPI 没有指定监视程序与前端间的传输协议。作为替代,工具厂商可以自己设计适合前端需要的监视程序代理。 

当为了允许监视程序代理和前端驻留在不同的机器中而设计传输协议时,需要考虑下列问题: 

指针大小(例如 32 或 64 位)- 所有 JVMPI ID 均是指针类型(参见数据类型)。 
字节顺序(小 endian 或大 endian)。 
位顺序(最重要的位在前,或者最不重要的位优先在前)。 
字符串编码 - JVMPI 使用 Java 虚拟机规范中列出的 UTF-8 编码。 
例如,与 JDK 1.2 版一起发布的 hprof 监视程序代理将所有标识符大小作为第一条记录发送,并对整数和浮点数据使用标准网络字节顺序。 

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 不同,监视程序代理也可传递参数,后者将提供给定事件类型的特定附加信息。 
虚拟机启动时,所有事件均将被禁用。一旦启用后,则在被显式禁用前该事件将保持可用状态。 

如果 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 不同,监视程序也可以传递参数,后者可提供给定事件类型的特定附加信息。 
虚拟机启动时,所有事件君将被禁用。一旦启用后,则在显式禁用前事件将保持启用状态。 

如果 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 结构分配足够的内存。虚拟机将填写 frames 缓冲区和 num_frames 域。 
参数: 

trace  - 跟踪由虚拟机填写的数据结构。  
depth  - 调用栈跟踪深度。  


jlong (*GetCurrentThreadCpuTime)(void);
由监视程序代理调用,用于获得当前线程消耗的累积 CPU 时间。 
返回值: 

以纳秒计的时间  


jobjectID (*GetMethodClass)(jmethodID mid);
由监视程序代理调用,用于获得定义方法的类的对象标识符。 
监视程序在调用该函数前必须禁用 GC。 

参数: 

mid  -方法标识符。  

返回值: 

定义类的对象标识符。  


void * (*GetThreadLocalStorage)(JNIEnv *env_id);
由监视程序调用,用于获得 JVMPI 局部线程存储值。JVMPI 将为代理提供指针大小的局部线程存储空间,能用来记录每个线程的监视程序信息。 
参数: 

env_id  - 线程的 JNIEnv *。  

返回值: 

局部线程存储空间的值  


jobjectID (*GetThreadObject)(JNIEnv *env);
由监视程序代理调用,用于获得相应于 JNIEnv 指针的线程对象标识符。 
监视程序在调用该函数前必须禁用 GC。 

参数: 

env  - 线程的 JNIEnv 指针。  

返回值: 

线程对象标识符。  


jint (*GetThreadStatus)(JNIEnv *env);
由监视程序代理调用,用于获得线程状态。 
JVMPI 函数 SuspendThread 和 ResumeThread 将不影响由 GetThreadStatus 返回的状态。通过 JVMPI 挂起的线程的状态将保持不变,并返回挂起时的状态。 

参数: 

env  - 线程的 JNIEnv *  

返回值: 

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);
由虚拟机调用,用于将事件发送给监视程序代理。监视程序代理通过调用 EnableEvent 注册它感兴趣的事件类型,或通过调用 RequestEvent 请求特定的事件类型。 
当 EnableEvent 启用事件时,产生该事件的线程就是发送该事件的线程。当 RequestEvent 请求事件时,请求事件的线程是发送该事件的线程。多个线程可以同时发送多个事件。 

如果事件特定信息含有 jobjectID,则可在禁用 GC 的情况下调用该函数。函数返回后将启用 GC。 

分配给 JVMPI_Event 结构的空间和任何事件特定信息在函数返回时将由虚拟机释放。监视程序代理必须复制任何需要保留在其内部缓冲区中的必要数据。 

参数: 

event  - 从虚拟机发送到监视程序代理的 JVMPI 事件。  


void (*ProfilerExit)(jint err_code);
由监视程序代理调用,用于通知虚拟机:监视程序想要退出并将错误代码设置为 err_code。该函数将导致虚拟机也以相同的 err_code 退出。 
参数: 

err_code  - 退出代码  


JVMPI_RawMonitor (*RawMonitorCreate)(char *lock_name);
由监视程序调用,用于创建原监控器。 
原监控器类似于 Java 监控器,差别在于原监控器不与 Java 对象发生关联关系。 

对于监视程序代理,在线程挂起模式中调用该函数是不安全的,因为该函数可以调用任意的系统函数(例如 malloc)和内部系统库锁定中的模块。 

如果原监控器名字是用下划线(“_”)开头的,则其监控器竞争事件不会发送给监视程序代理。 

参数: 

lock_name  - 原监控器名。  

返回值: 

原监控器  


void (*RawMonitorDestroy)(JVMPI_RawMonitor lock_id);
由监视程序代理调用,用于销毁原监控器并释放所有与监控器相关联的系统资源。 
原监控器与 Java 监控器类似,差别在于原监控器不与 Java 对象发生关联关系。 

对于监视程序代理,在线程挂起模式中调用该函数是不安全的,因为该函数可以调用任意的系统函数(例如 free)和内部系统库锁定中的模块。 

参数: 

lock_id  - 要销毁的原监控器  


void (*RawMonitorEnter)(JVMPI_RawMonitor lock_id);
由监视程序代理调用,用于进入原监控器。 
原监控器与 Java 监控器类似,差别在于原监控器不与 Java 对象发生关联关系。 

对于监视程序代理,在线程挂起模式中调用该函数是不安全的,因为当前的线程可能阻塞已挂起线程获得的原监控器。 

参数: 

lock_id  - 要进入的原监控器  


void (*RawMonitorExit)(JVMPI_RawMonitor lock_id);
由监视程序代理调用,用于退出原监控器。 
原监控器与 Java 监控器类似,差别在于原监控器不与 Java 对象发生关联关系。 

参数: 

lock_id  - 要退出的原监控器  


void (*RawMonitorNotifyAll)(JVMPI_RawMonitor lock_id);
由监视程序调用,用于通知所有等待原监控器的线程。 
原监控器与 Java 监控器类似,差别在于原监控器不与 Java 对象发生关联关系。 

参数: 

lock_id  - 要通知的原监控器  


void (*RawMonitorWait)(JVMPI_RawMonitor lock_id, jlong ms);
由监视程序代理调用,用于等待原监控器指定超时时间。将 0 作为超时时间传递会导致线程永久等待。 
原监控器与 Java 监控器类似,差别在于原监控器不与 Java 对象发生关联关系。 

参数: 

lock_id  - 要等待的原监控器  
ms  - 等待时间(以毫秒计)。  


jint (*RequestEvent)(jint event_type, void *arg);
由监视程序代理调用,用于请求特定类型的通知事件。与 event_type 不同,监视程序代理也可传递参数,后者可提供给定事件类型的特定附加信息。 
可以调用该函数来请求一次事件,例如 JVMPI_EVENT_HEAP_DUMP、JVMPI_EVENT_MONITOR_DUMP 和JVMPI_EVENT_OBJECT_DUMP。 这些事件的通知无法用 EnableEvent 和 DisableEvent 函数控制。 

另外,可以调用该函数来请求特定类、线程或对象的定义事件。当监视程序代理需要解决事件中所接收到的未知类、方法、线程或对象标识符,而前面已禁用相应的定义事件时,它将非常有用。 

监视程序代理可通过请求 JVMPI_EVENT_CLASS_LOAD 事件并将事件特定参数设置为类对象标识符,来接收有关未知类标识符的信息。 
监视程序代理可通过请求 JVMPI_EVENT_THREAD_START 事件并将事件特定参数设置成线程对象标识符,来接受有关未知线程标识符的信息。 
监视程序代理可通过请求 JVMPI_EVENT_OBJECT_ALLOC 事件并将事件特定参数设置成对象标识符,来接受有关未知对象标识符的信息,。 
因此,监视程序可通过调用 EnableEvent 异步启用上述三种事件,也可通过调用 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 (*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 将保持无效。 

参数; 

env  - 线程 JNIEnv *。  


jboolean (*ThreadHasRun)(JNIEnv *env);
由监视程序代理调用,用于决定给定的 JNIEnv 指针标识自从线程被 SuspendThread 挂起后是否消耗了 CPU 时间。当线程由 ResumeThread 恢复并接着由 SuspendThread 函数挂起时,必须调用该函数。 
参数: 

env  - 线程 JNIEnv *。  

返回值: 

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;


内容: 

arena_id  - 删除块的标识符。  


JVMPI_EVENT_ARENA_NEW
为分配对象创建新块时发送。 
struct {
    jint arena_id;
    char *arena_name;
} new_arena;


内容: 

arena_id  - 给定块的标识符。  
arena_name  - 块名。  


JVMPI_EVENT_CLASS_LOAD
当在虚拟机中加载类或当监视程序代理通过发布 RequestEvent 调用请求 JVMPI_EVENT_CLASS_LOAD 事件时发送。对于后一种情况,将设置事件类型中的 JVMPI_REQUESTED_EVENT 位。 
该事件可在禁用 GC 的情况下发出。NotifyEvent   GC。 

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  - 该类实现的接口数。  
方法  - 该类中定义的方法。  
num_static_fields  - 该类中定义的静态域数。  
静态域  - 该类中定义的静态域。  
num_instance_fields  - 该类中定义的实例域数。  
实例  - 该类中定义的实例域。  
class_id  - 类对象标识符。  

注意:类标识符是类对象的标识符,并将在 JVMPI_EVENT_OBJECT_MOVE 发生时改变。 


JVMPI_EVENT_CLASS_LOAD_HOOK
当虚拟机获得类文件数据,但同时又尚未构造该类的内存表示前发送。监视程序代理可让虚拟机发送的现有类文件数据中包括监视程序钩子。 
监视程序必须使用在该事件中发送的内存分配函数指针为更改的类文件数据缓冲区分配空间,因为虚拟机负责着释放新类文件的数据缓冲区。 

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 的情况下发出。NotifyEvent  GC。 

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
由虚拟机发送,用于请求监视程序代理转储其数据。这仅是一种暗示且监视程序代理无需响应该事件。它对于来自用户的命令行信号处理非常有用。例如在 JDK 1.2 中,Win32 中的 CTRL-Break 和 Solaris 中的 CTRL-\ 即帮助虚拟机将该事件发送给监视程序代理。 
没有事件特定的信息。 


JVMPI_EVENT_DATA_RESET_REQUEST
由虚拟机发送,用于请求监视程序代理重新设置其数据。这仅是一种暗示且监视程序代理无需响应该事件。它对于来自用户的命令行信号处理非常有用。例如在 JDK 1.2 中,Win32 中的 CTRL-Break 和 Solaris 中的 CTRL-\ 即帮助虚拟机将该事件发送给监视程序代理。 
没有事件特定的信息。 


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 函数请求时发送。监视程序代理可通过将 JVMPI_HeapDumpArg 结构作为第二个参数传给 RequestEvent,同时将 heap_dump_level 域设为想要的转储级别来指定所要转储信息的级别。 
转储级别值可以为: 

JVMPI_DUMP_LEVEL_0 
JVMPI_DUMP_LEVEL_1 
JVMPI_DUMP_LEVEL_2 
如果传递 NULL 值,则转储级别将设置为 JVMPI_DUMP_LEVEL_2。 

该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

事件特定的数据中包含 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 根所驻留的栈跟踪数,0 表示 JVMPI_DUMP_LEVEL_0  
跟踪  - GC 根所驻留的栈跟踪  

begin 和 end 之间的堆转储格式取决于请求的信息级别,其格式将在 JVMPI 转储格式一节中详细介绍。 


JVMPI_EVENT_JNI_GLOBALREF_ALLOC
创建 JNI 全局引用时发送。事件特定的数据包含 JNI 全局引用和相应的对象标识符。 
该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

struct {
    jobjectID obj_id;
    jobject ref_id;
} jni_globalref_alloc;

内容: 

obj_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 弱全局引用和相应的对象标识符。 
该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

struct {
    jobjectID obj_id;
    jobject ref_id;
} jni_globalref_alloc;

内容: 

obj_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
初始化完成时由虚拟机发送。仅当通知该事件之后调用 CreateSystemThread 才是安全的。 
无事件特定的数据。 


JVMPI_EVENT_JVM_SHUT_DOWN
当虚拟机关闭时由改虚拟机发送。监视程序通常将保存监视程序数据。 
无事件特定的数据。 


JVMPI_EVENT_METHOD_ENTRY
当进入方法时发送。与 JVMPI_EVENT_METHOD_ENTRY2 相比,该事件不发送调用其方法的目标对象的 jobjectID。 
struct {
    jmethodID method_id;
} method;

内容: 

method_id  - 要进入的方法。  


JVMPI_EVENT_METHOD_ENTRY2
当进入方法时发送。如果方法是实例方法,则目标对象的 jobjectID 与该事件一起发送。如果方法是静态方法,则该事件中的 obj_id 域将被设置为 NULL。 
该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

struct {
    jmethodID method_id;
    jobjectID obj_id;
} method_entry2;

内容: 

method_id  - 要进入的方法。  
obj_id  - 目标对象,静态方法为 NULL。  


JVMPI_EVENT_METHOD_EXIT
当退出方法时发送。方法退出可以是正常退出,也可以是由未处理异常引起的退出。 
struct {
    jmethodID method_id;
} method;

内容: 

method_id  - 要进入的方法。  


JVMPI_EVENT_MONITOR_CONTENDED_ENTER
当线程试图进入已经由另一线程获得的 Java 监控器时发送。 
该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

struct {
    jobjectID object;
} monitor;

内容: 

object  - 与监控器相关联的对象标识符  


JVMPI_EVENT_MONITOR_CONTENDED_ENTERED
当线程在等待被另一线程释放后进入 Java 监控器时发送。 
该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

struct {
    jobjectID object;
} monitor;

内容: 

object  - 与监控器相关联的对象标识符  


JVMPI_EVENT_MONITOR_CONTENDED_EXIT
当线程退出 Java 监控器,同时另一线程正等待获得相同监控器时发送。 
该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

struct {
    jobjectID object;
} monitor;

内容: 

object  - 与监控器相关联的对象标识符  


JVMPI_EVENT_MONITOR_DUMP
当由 RequestEvent 函数请求时发送。 
事件特定的数据包含虚拟机中所有线程和监控器的快照。 

该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

struct {
    char *begin;
    char *end;
    jint num_traces;
    JVMPI_CallTrace *traces;
    jint *threads_status;
} monitor_dump;

内容: 

begin  - 监控器转储缓冲区开始。  
end  - 转储缓冲区结束  
num_traces  - 线程跟踪数。  
traces  - 所有线程的跟踪。  
thread_status  - 所有线程的状态。  

监控器转储缓冲区格式将在 JVMPI 转储格式一节中详细介绍。 


JVMPI_EVENT_MONITOR_WAIT
当线程要等待对象时发送。 
该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

struct {
    jobjectID object;
    jlong timeout;
} monitor_wait;

内容: 

object  - 当前线程等待的对象标识符。  
 (NULL 指示线程处于 Thread.sleep)  
timeout  - 线程等待的毫秒数(0 表示永久等待)。  


JVMPI_EVENT_MONITOR_WAITED
当线程完成等待对象时发送。 
该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

struct {
    jobjectID object;
    jlong timeout;
} monitor_wait;

内容: 

object  - 当前线程等待的对象标识符。  
 (NULL 指示线程处于 Thread.sleep中)  
timeout  - 线程等待的毫秒数。  


JVMPI_EVENT_OBJECT_ALLOC
当分配对象或当监视程序通过发出 RequestEvent 调用请求 JVMPI_EVENT_OBJECT_ALLOC 事件时发送。对于后一种情况,将设置事件类型中的 JVMPI_REQUESTED_EVENT 位。 
该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

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 布尔数组  
JVMPI_BYTE 字节数组  
JVMPI_CHAR 字符数组  
JVMPI_SHORT 短整数数组  
JVMPI_INT 整数数组  
JVMPI_LONG 长整数数组  
JVMPI_FLOAT 浮点型数组  
JVMPI_DOUBLE 双精度型数组  
 
size  - 字节数。  
obj_id  - 唯一的对象标识符。  


JVMPI_EVENT_OBJECT_DUMP
当 RequestEvent 函数请求时发送。请求转储对象的 jobjectID 应作为第二参数传递给 RequestEvent。 
监视程序代理应在禁用 GC 后再请求该事件。 

事件特定的数据包含对象的快照。 

struct {
    jint data_len;
    char *data;
} object_dump;

内容: 

data_len  - 对象转储缓冲区的长度  
data  - 对象转储开始  

对象转储缓冲区的格式将在 JVMPI 转储格式一节中详细介绍。 


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  - 当前对象标识符。  
new_arena_id  - 新块。  
new_obj_id  - 新对象标识符。  


JVMPI_EVENT_RAW_MONITOR_CONTENDED_ENTER
当线程试图进入已被另一线程获得的原监控器时发送。 
struct {
    char *name;
    JVMPI_RawMonitor id;
} raw_monitor;

内容: 

name  - 原监控器名  
id  - 原监控器标识符  


JVMPI_EVENT_RAW_MONITOR_CONTENDED_ENTERED
当线程在等待被另一线程释放后进入原监控器时发送。 
struct {
    char *name;
    JVMPI_RawMonitor id;
} raw_monitor;

内容: 

name  - 原监控器名  
id  - 原监控器标识符  


JVMPI_EVENT_RAW_MONITOR_CONTENDED_EXIT
当线程退出原监控器且另一线程在等待获得相同的监控器时发送。 
struct {
    char *name;
    JVMPI_RawMonitor id;
} raw_monitor;

内容: 

name  - 原监控器名  
id  - 原监控器标识符  


JVMPI_EVENT_THREAD_END
当线程在虚拟机中结束时发送。 
该事件通知中所接收的 JVMPI_Event 的 env_id 域是要结束线程的 JNIEnv 接口指针。 


JVMPI_EVENT_THREAD_START
当线程在虚拟机中启动,或当监视程序代理通过发出 RequestEvent 调用请求 JVMPI_EVENT_THREAD_START 事件时发送。对于后一种情况,将设置事件类型中的 JVMPI_REQUESTED_EVENT 位。 
该事件可在禁用 GC 的情况下发出。NotifyEvent 返回后将重新启用 GC。 

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  - 线程对象标识符。  
thread_env_id  - 线程的 JNIEnv *。  

线程与 JNIEnv 指针和线程对象标识符相关联。JVMPI 使用 JNIEnv 指针作为线程标识符。 


4. 转储格式

4.1 转储格式描述中使用的大小和类型
u1: 1 字节  
u2: 2 字节  
u4: 4 字节  
u8: 8 字节  
ty: u1 其中: 
 JVMPI_NORMAL_OBJECT牋 普通对象  
JVMPI_CLASS 对象数组  
JVMPI_BOOLEAN 布尔数组  
JVMPI_BYTE 字节数组  
JVMPI_CHAR 字符数组  
JVMPI_SHORT 短整数数组  
JVMPI_INT 整数数组  
JVMPI_LONG 长整数数组  
JVMPI_FLOAT 浮点型数组  
JVMPI_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 基本相同,但转储中不包括下列值:对象实例转储中的基本域、类转储中的基本静态域和基本数组元素。 

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  保护域  
void *  保留  
void *  保留  
u4  实例大小(以字节计)  
[jobjectID]*  接口  
u2  常量缓冲池大小  
[u2,  常量缓冲池索引,  
爐y,  类型,  
爒l]*  值  
[vl]*  静态域值  
 
JVMPI_GC_INSTANCE_DUMP 
(正常对象转储)  jobjectID 对象  
jobjectID 类  
u4  后面的字节数  
[vl]*  实例域值(类,后跟超类,超类的超类 ...)  
 
JVMPI_GC_OBJ_ARRAY_DUMP 
(对象数组转储)  jobjectID  数组对象  
u4  元素数  
jobjectID  元素类标识符(在 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 相同,包括下面所有值:对象实例转储中的基本域、类转储中的基本静态域和基本数组元素。 

4.4 监控器转储格式
转储缓冲区由记录序列组成,其中每个记录包括一个 8 位记录类型,后跟相应记录类型的数据。 

记录类型  记录数据  
JVMPI_MONITOR_JAVA 
(Java 监控器)  jobjectID  对象标识符  
JNIEnv *  业主线程  
u4  项计数  
u4  等待进入的线程数  
[JNIEnv *]*  等待进入的线程  
u4  等待通知的线程数  
[JNIEnv *]*  等待通知的线程  
 
JVMPI_MONITOR_RAW 
(原监控器)  char *  原监控器名  
JVMPI_RawMonitor  原监控器标识符  
JNIEnv *  业主线程  
u4  项计数  
u4  等待进入的线程数  
[JNIEnv *]*  等待进入的线程  
u4  等待通知的线程数  
[JNIEnv *]*  等待通知的线程  
 


5. 数据类型
字符使用 Java 虚拟机规范中的 UTF-8 编码方式进行编码。 

jobjectID
代表对象标识符的不透明指针。 
struct _jobjectID;
typedef struct _jobjectID * jobjectID;


JVMPI_CallFrame
要执行的方法。 
typedef struct {
    jint lineno;
    jmethodID method_id;
} JVMPI_CallFrame;

域: 

行号  - 源文件里的行号。  
method_id  - 要执行的方法。  


JVMPI_CallTrace
方法所执行的调用跟踪。 
typedef struct {
    JNIEnv *env_id;
    jint num_frames;
    JVMPI_CallFrame *frames;
} JVMPI_CallTrace;

域: 

env_id  - 执行该跟踪的线程标识符。  
num_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  - 赋予该方法的标识符  


JVMPI_RawMonitor
代表原监控器的不透明指针。 
struct _JVMPI_RawMonitor;
typedef struct _JVMPI_RawMonitor * JVMPI_RawMonitor;


6. JDK 1.2 实现局限性的说明
对象数组的 JVMPI_EVENT_OBJECT_ALLOC 事件发布时的元素类标识符未知(例如 class_id 域总是 NULL)。 
在 Win32 中,JIT 编译器还不支持下列事件: 
JVMPI_EVENT_METHOD_ENTRY、 
JVMPI_EVENT_METHOD_ENTRY2、 
JVMPI_EVENT_METHOD_EXIT、 
JVMPI_EVENT_COMPILED_METHOD_LOAD 和 
JVMPI_EVENT_COMPILED_METHOD_UNLOAD。 
必须在禁用 GC 后再调用 SuspendThread。在所有线程恢复前,GC 必须保持禁用状态。 
主线程(虚拟机创建的第一个线程)的线程启动事件可在其他引用 JNIEnv 接口指针的事件之后到达。 
永远不要发布 JVMPI_EVENT_ARENA_NEW 和 JVMPI_EVENT_ARENA_DELETE 事件。其它事件中的块标识符应始终设置为 1。 
--------------------------------------------------------------------------------

上次更新时间:1998 年 11 月 11 日,星期三,14:14:44 PST