什么是 JVM 工具接口?
虚拟机TM工具接口(JVM TI) 是开发和监控工具使用的编程接口。它提供了一种检查状态和控制在 Java 中运行的应用程序执行的方法TM虚拟机 (VM)。
JVM TI 旨在为需要访问 VM 状态的各种工具提供 VM 接口,包括但不限于:分析、调试、监控、线程分析和重写率分析工具。
JVM TI 可能不适用于 Java 的所有实现TM虚拟机。
JVM TI 是双向接口。 JVM的客户端 TI,以下称为代理人可以通过 events 通知有趣的事件。虚拟机 TI 可以通过许多 功能 查询和控制应用程序,以响应事件或独立于事件。
代理与执行正在检查的应用程序的虚拟机在同一进程中运行并直接通信。这种通信是通过本机接口(JVM 钛)。本机进程内接口允许最大程度地控制,同时将工具部分的干扰降至最低。通常,代理相对紧凑。它们可以由一个单独的进程控制,该进程实现工具的大部分功能,而不会干扰目标应用程序的正常执行。
架构
工具可以直接写入JVM TI 或间接通过更高级别的接口。 Java 平台调试器架构包括 JVM TI,还包含更高级别的进程外调试器接口。高层接口比JVM更合适 TI 适用于许多工具。有关 Java 平台调试器体系结构的更多信息,请参阅 Java 平台调试器架构网站。
代笔
代理可以用支持 C 语言调用约定和 C 或 C++ 定义的任何本地语言编写。
使用 JVM 所需的函数、事件、数据类型和常量定义 TI 在包含文件 jvmti.h
中定义。要使用这些定义,请添加 J2SETM包含目录到您的包含路径并添加
#include <jvmti.h>
到你的源代码。
部署代理
代理以特定于平台的方式部署,但通常是动态库的平台等价物。在 Windows 上TM操作系统,例如,代理库是“动态链接库”(DLL)。在 Linux 上TMOperating Environment,代理库是一个共享对象(.so
文件)。
通过使用 命令行选项 指定代理程序库名称,可以在 VM 启动时启动代理程序。某些实现可能支持在实时 阶段 中使用 启动代理 的机制。这是如何启动的细节是特定于实现的。
静态链接代理(自版本 1.2.3 起)
本机 JVMTI 代理可能是静态链接与虚拟机。库和 VM 映像的组合方式取决于实现。其图像已与 VM 组合的智能体 L 定义为静态链接当且仅当代理导出名为 Agent_OnLoad_L 的函数时。
如果一个静态链接agent L 导出了一个名为 Agent_OnLoad_L 的函数和一个名为 Agent_OnLoad 的函数,Agent_OnLoad 函数将被忽略。如果代理 L 是静态链接, 将使用为 Agent_OnLoad 函数指定的相同参数和预期返回值调用 Agent_OnLoad_L 函数。代理 L 是静态链接将禁止动态加载同名代理。
VM 将在 VM 执行期间调用动态入口点 Agent_OnUnLoad 的同一点调用代理的 Agent_OnUnload_L 函数(如果导出此类函数)。无法卸载静态加载的代理。 Agent_OnUnload_L 函数仍将被调用以执行任何其他代理关闭相关任务。如果一个静态链接agent L 导出一个名为 Agent_OnUnLoad_L 的函数和一个名为 Agent_OnUnLoad 的函数,Agent_OnUnLoad 函数将被忽略。
如果代理 L 是静态链接, 将使用为 Agent_OnAttach 函数指定的相同参数和预期返回值调用 Agent_OnAttach_L 函数。如果一个静态链接代理 L 导出一个名为 Agent_OnAttach_L 的函数和一个名为 Agent_OnAttach 的函数,Agent_OnAttach 函数将被忽略。
代理命令行选项
下面使用术语“命令行选项”来表示 JNI 调用 API 的 JavaVMInitArgs
参数中提供给 JNI_CreateJavaVM
函数的选项。
以下两个命令行选项之一用于 VM 启动以正确加载和运行代理。这些参数标识包含代理的库以及要在启动时传入的选项字符串。
-
-agentlib:
<代理库名称>=
<选项>
-agentlib:
后面的名称是要加载的库的名称。库的查找,包括其全名和位置,以特定于平台的方式进行。通常,<代理库名称>扩展为操作系统特定的文件名。这<选项>将在启动时传递给代理。例如,如果指定选项-agentlib:foo=opt1,opt2
,则VM将尝试从Windows下的系统PATH
加载共享库foo.dll
TM或 libfoo.so
来自 Linux 下的 LD_LIBRARY_PATH
TM.如果代理库静态链接到可执行文件,则不会发生实际加载。
-
-agentpath:
<path-to-agent>=
<选项>
-agentpath:
之后的路径是加载库的绝对路径。不会发生库名称扩展。这<选项>将在启动时传递给代理。例如,如果指定选项 -agentpath:c:\myLibs\foo.dll=opt1,opt2
,VM 将尝试加载共享库 c:\myLibs\foo.dll
。如果代理库静态链接到可执行文件,则不会发生实际加载。
对于动态共享库代理,将调用库中的启动例程Agent_OnLoad
。如果代理库静态链接到可执行文件,则系统将尝试调用 Agent_OnLoad_<agent-lib-name>
入口点,其中 <agent-lib-name> 是代理的基本名称。在上面的示例 -agentpath:c:\myLibs\foo.dll=opt1,opt2
中,系统将尝试查找并调用 Agent_OnLoad_foo
启动例程。
加载了 -agentlib:
或 -agentpath:
的库将搜索 JNI 本机方法实现,以方便在工具中使用 Java 编程语言代码,正如 字节码检测 所需要的那样。
在搜索完所有其他库后,将搜索代理库(希望覆盖或拦截非代理方法的本机方法实现的代理可以使用 NativeMethodBind 事件 )。
这些开关仅执行上述操作 - 它们不会更改 VM 或 JVM 的状态 TI。启用 JVM 不需要命令行选项 TI 或 JVM 方面 TI,这是通过使用 capabilities 以编程方式处理的。
代理启动
VM 通过调用启动函数来启动每个代理。如果在 OnLoad
阶段 中启动代理,将调用用于静态链接代理的函数 Agent_OnLoad
或 Agent_OnLoad_L
。如果代理在 live 阶段 中启动,将调用静态链接代理的函数 Agent_OnAttach
或 Agent_OnAttach_L
。每个代理只调用一次启动函数。
代理启动(加载阶段)
如果代理在 OnLoad
阶段启动,则其代理库必须导出具有以下原型的启动函数:
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
或者对于名为“L”的静态链接代理:
JNIEXPORT jint JNICALL
Agent_OnLoad_L(JavaVM *vm, char *options, void *reserved)
VM 将通过调用此函数来启动代理。它将在 VM 初始化时足够早地被调用:
VM 将调用 Agent_OnLoad
或 Agent_OnLoad_<agent-lib-name>
函数<选项>作为第二个参数 - 也就是说,使用命令行选项示例,"opt1,opt2"
将传递给 Agent_OnLoad
的 char *options
参数。 options
参数被编码为 修改后的 UTF-8 字符串。如果=<选项>未指定,零长度字符串将传递给 options
。 options
字符串的生命周期是 Agent_OnLoad
或 Agent_OnLoad_<agent-lib-name>
调用。如果需要超出此时间,则必须复制字符串或字符串的一部分。 Agent_OnLoad
被调用和它返回之间的时间段称为加载阶段.由于 VM 在 OnLoad 阶段 期间未初始化,因此 Agent_OnLoad
中允许的操作集受到限制(请参阅此时可用功能的功能描述)。代理可以安全地处理选项并使用 SetEventCallbacks
设置事件回调。一旦收到 VM 初始化事件(即调用 虚拟机初始化 回调),代理就可以完成其初始化。
理由:需要提前启动,以便代理可以设置所需的功能,其中许多功能必须在 VM 初始化之前设置。在 JVMDI 中,-Xdebug 命令行选项提供了非常粗粒度的功能控制。 JVMPI 实现使用各种技巧来提供单个“JVMPI on”开关。没有合理的命令行选项可以提供平衡所需功能与性能影响所需的精细控制。还需要提前启动,以便代理可以控制执行环境——修改文件系统和系统属性以安装它们的功能。
Agent_OnLoad
或 Agent_OnLoad_<agent-lib-name>
的返回值用于指示错误。除零以外的任何值都表示错误并导致 VM 终止。
代理启动(实时阶段)
VM 可能支持一种机制,允许代理在实时 阶段 期间在 VM 中启动。如何支持这一点的细节是特定于实现的。例如,一个工具可以使用一些特定于平台的机制或特定于实现的 API 来附加到正在运行的 VM,并请求它启动给定的代理。
如果代理在实时阶段启动,则其代理库必须导出具有以下原型的启动函数:
JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM* vm, char *options, void *reserved)
或者对于名为“L”的静态链接代理:
JNIEXPORT jint JNICALL
Agent_OnAttach_L(JavaVM* vm, char *options, void *reserved)
VM 将通过调用此函数来启动代理。它将在附加到 VM 的线程的上下文中调用。第一个参数<虚拟机>是 Java 虚拟机。这<选项>参数是提供给代理的启动选项。<选项>被编码为 修改后的 UTF-8 字符串。如果未提供启动选项,则会将零长度字符串传递给 options
。 options
字符串的生命周期是 Agent_OnAttach
或 Agent_OnAttach_<agent-lib-name>
调用。如果需要超出此时间,则必须复制字符串或字符串的一部分。
请注意,某些 capabilities 可能无法在实时阶段使用。
Agent_OnAttach
或 Agent_OnAttach_<agent-lib-name >
函数初始化代理并向 VM 返回一个值以指示是否发生错误。任何非零值都表示错误。错误不会导致 VM 终止。相反,VM 会忽略错误,或采取一些特定于实现的操作——例如,它可能会向标准错误打印错误,或在系统日志中记录错误。
代理关闭
该库可以选择导出具有以下原型的关闭函数:
JNIEXPORT void JNICALL
Agent_OnUnload(JavaVM *vm)
或者对于名为“L”的静态链接代理:
JNIEXPORT void JNICALL
Agent_OnUnload_L(JavaVM *vm)
该函数将在库即将卸载时由 VM 调用。库将被卸载(除非它被静态链接到可执行文件中)并且如果某些特定于平台的机制导致卸载(本文档中未指定卸载机制)或库(实际上)被卸载,则将调用此函数VM 的终止。 VM 终止包括正常终止和 VM 故障,包括启动失败,但当然不包括非受控关闭。如果 Agent_OnAttach
/ Agent_OnAttach_L
函数报告错误(返回非零值),实现也可以选择不调用此函数。请注意此函数与 虚拟机死亡事件 之间的区别:要发送 VM 死亡事件,VM 必须至少运行到初始化点和有效的 JVM 必须存在已为 VMDeath 设置回调并启用事件的 TI 环境。 Agent_OnUnload
或 Agent_OnUnload_<agent-lib-name>
不需要这些,如果库因其他原因被卸载,也会调用此函数。在发送 VM Death 事件的情况下,它将在调用此函数之前发送(假设由于 VM 终止而调用此函数)。此函数可用于清理代理分配的资源。
由于不能始终访问或修改命令行,例如在嵌入式 VM 中或只是在脚本深处启动 VM,因此提供了一个 JAVA_TOOL_OPTIONS
变量,以便在这些情况下可以启动代理。
支持环境变量或其他命名字符串的平台可能支持 JAVA_TOOL_OPTIONS
变量。该变量将在空白边界处分解为选项。空白字符包括空格、制表符、回车符、换行符、垂直制表符和换页符。空白字符序列被认为等同于单个空白字符。除非引用,否则选项中不包含空格。报价如下:
- 一对单引号 ('') 之间的所有字符(单引号除外)都被引用。
- 双引号字符在一对单引号内没有特殊含义。
- 除双引号外,所有包含在一对双引号 ("") 之间的字符都被引用。
- 一对双引号内的单引号字符没有特殊含义。
- 引用部分可以在变量的任何地方开始或结束。
- 空白字符在引用时没有特殊含义——它们像任何其他字符一样包含在选项中,并且不标记空白边界。
- 该对引号不包含在选项中。
JNI_CreateJavaVM
(在 JNI 调用 API 中)会将这些选项添加到其 JavaVMInitArgs
参数中提供的选项之前。在担心安全问题的情况下,平台可能会禁用此功能;例如,当有效用户或组 ID 与真实 ID 不同时,参考实现会在 Unix 系统上禁用此功能。此功能旨在支持工具的初始化——具体包括启动本机或 Java 编程语言代理。多个工具可能希望使用此功能,因此不应覆盖变量,而是应将选项附加到变量。请注意,由于变量是在 JNI 调用 API 创建 VM 调用时处理的,因此不会处理启动器处理的选项(例如,VM 选择选项)。
环境
虚拟机 TI规范支持同时使用多个JVM 德州仪器代理商。每个代理都有自己的 JVM 德州仪器环境。也就是说,JVM TI 状态对于每个代理都是独立的 - 对一个环境的更改不会影响其他环境。 JVM 的状态 TI 环境包括:
虽然他们的 JVM TI 状态是独立的,代理检查和修改 VM 的共享状态,它们还共享它们执行的本机环境。因此,一个代理可以扰乱其他代理的结果或导致它们失败。指定与其他代理的兼容性级别是代理编写者的责任。虚拟机 TI 实现无法防止代理之间的破坏性交互。减少这些事件发生可能性的技术超出了本文档的范围。
代理创建 JVM TI环境通过一个JVM TI 版本作为 JNI 调用 API 函数 GetEnv 的接口 ID。有关创建和使用 JVM 的更多详细信息,请参阅 Accessing JVM TI Functions 德州仪器环境。通常,JVM TI 环境是通过从 Agent_OnLoad
调用 GetEnv
创建的。
字节码检测
该接口不包括一些在具有分析支持的接口中可能会发生的事件。一些示例包括全速方法进入和退出事件。该接口改为提供对字节码检测,改变构成目标程序的 Java 虚拟机字节码指令的能力。通常,这些更改是将“事件”添加到方法的代码中 - 例如,在方法的开头添加对 MyProfiler.methodEntered()
的调用。由于更改纯粹是附加的,因此它们不会修改应用程序状态或行为。因为插入的代理代码是标准字节码,VM 可以全速运行,不仅优化了目标程序,还优化了检测。如果检测不涉及从字节码执行切换,则不需要昂贵的状态转换。结果是高性能事件。这种方法还为代理提供了完全控制:检测可以限制在代码的“有趣”部分(例如,最终用户的代码)并且可以是有条件的。 Instrumentation 可以完全以 Java 编程语言代码运行,也可以调用本地代理。仪器可以简单地维护计数器或可以对事件进行统计采样。
可以通过以下三种方式之一插入仪器:
- 静态检测:类文件在加载到 VM 之前进行检测 - 例如,通过创建
*.class
文件的重复目录,这些文件已被修改以添加检测。这种方法非常笨拙,一般来说,代理无法知道将要加载的类文件的来源。
- Load-Time Instrumentation:当 VM 加载类文件时,类文件的原始字节被发送到代理进行检测。由类加载触发的
ClassFileLoadHook
事件提供了此功能。这种机制提供了对一次性检测的有效和完整的访问。
- 动态检测:修改已经加载(甚至可能正在运行)的类。此可选功能由
ClassFileLoadHook
事件提供,通过调用 RetransformClasses
函数触发。类可以修改多次,也可以恢复到原来的状态。该机制允许在执行过程中更改仪器。
此接口中提供的类修改功能旨在提供一种检测机制(ClassFileLoadHook
事件和 RetransformClasses
函数),以及在开发期间用于修复并继续调试(RedefineClasses
函数)的机制。
必须小心避免扰乱依赖性,尤其是在检测核心类时。例如,获取每个对象分配通知的方法是检测 Object
上的构造函数。假设构造函数最初为空,则构造函数可以更改为:
public Object() {
MyProfiler.allocationTracker(this);
}
但是,如果此更改是使用 ClassFileLoadHook
事件进行的,那么这可能会影响典型的 VM,如下所示:第一个创建的对象将调用构造函数,导致类加载 MyProfiler
;这将导致对象创建,并且由于 MyProfiler
尚未加载,无限递归;导致堆栈溢出。对此的改进是将调用跟踪方法延迟到安全时间。例如,可以在 VMInit
事件的处理程序中设置 trackAllocations
。
static boolean trackAllocations = false;
public Object() {
if (trackAllocations) {
MyProfiler.allocationTracker(this);
}
}
SetNativeMethodPrefix
允许通过使用包装器方法来检测本机方法。
模块中代码的字节码检测
代理可以使用函数 AddModuleReads
、 AddModuleExports
、 AddModuleOpens
、 AddModuleUses
和 AddModuleProvides
来更新模块以扩展它读取的模块集、它导出或打开给其他模块的包集,或者它使用和提供的服务。
作为在引导类加载器的搜索路径或加载主类的类加载器的搜索路径上部署支持类的代理的帮助,Java 虚拟机安排由ClassFileLoadHook
事件转换的类模块读取两个类加载器的未命名模块。
修改后的 UTF-8 字符串编码
JVM TI 使用修改后的 UTF-8 对字符串进行编码。这与 JNI 使用的编码相同。修改后的 UTF-8 与标准 UTF-8 的区别在于补充字符和空字符的表示。有关详细信息,请参阅 JNI 规范的 修改后的 UTF-8 字符串 部分。
规范上下文
由于此接口提供了对 Java 虚拟机中运行的应用程序状态的访问;该术语指的是 Java 平台而不是本机平台(除非另有说明)。例如:
- “线程”是指Java 编程语言中的线程。
- “栈帧”是指Java虚拟机栈帧。
- “类”表示 Java 编程语言类。
- “堆”是指 Java 虚拟机堆。
- “监视器”是指 Java 编程语言对象监视器。
Sun、Sun Microsystems、Sun 徽标、Java 和 JVM 是 Oracle 和/或其附属公司在美国和其他国家/地区的商标或注册商标。
功能
访问函数
本机代码访问 JVM TI通过调用JVM的特性 德州仪器功能。访问 JVM TI 函数是通过使用接口指针以与Java 本机接口 (JNI) 函数 相同的方式访问的。虚拟机 TI接口指针被称为环境指针.
环境指针是指向环境的指针,类型为 jvmtiEnv*
。一个环境有关于它的 JVM 的信息 德州仪器连接。环境中的第一个值是指向函数表的指针。函数表是指向 JVM 的指针数组 德州仪器功能。每个函数指针都位于数组内的预定义偏移量处。
从 C 语言使用时:使用双重间接访问函数;环境指针提供上下文,是每个函数调用的第一个参数;例如:
jvmtiEnv *jvmti;
...
jvmtiError err = (*jvmti)->GetLoadedClasses(jvmti, &class_count, &classes);
从 C++ 语言使用时:函数作为 jvmtiEnv
的成员函数访问;环境指针未传递给函数调用;例如:
jvmtiEnv *jvmti;
...
jvmtiError err = jvmti->GetLoadedClasses(&class_count, &classes);
除非另有说明,本规范中的所有示例和声明均使用 C 语言。
A JVM TI环境可以通过JNI Invocation APIGetEnv
函数获取:
jvmtiEnv *jvmti;
...
(*jvm)->GetEnv(jvm, &jvmti, JVMTI_VERSION_1_0);
每次调用 GetEnv
都会创建一个新的 JVM TI 连接和新的 JVM 德州仪器环境。 GetEnv
的 version
参数必须是 JVM TI 版本。返回的环境可能与请求的版本不同,但返回的环境必须兼容。如果兼容版本不可用,GetEnv
将返回 JNI_EVERSION
,如果 JVM 不支持 TI 或 JVM 当前 VM 配置不支持 TI。可以添加其他接口用于创建 JVM 特定上下文中的 TI 环境。每个环境都有自己的状态(例如,期望的事件、事件处理函数 和 capabilities)。环境随 DisposeEnvironment
一起发布。因此,与每个线程只有一个环境的 JNI 不同,JVM TI 环境跨线程工作并且是动态创建的。
函数返回值
JVM TI 函数始终通过 jvmtiError
函数返回值返回 错误代码。一些函数可以通过调用函数提供的指针返回附加值。在某些情况下,JVM TI 函数分配您的程序必须显式释放的内存。这在单独的 JVM 中指示 TI 函数说明。空列表、数组、序列等作为 NULL
返回。
如果 JVM TI 函数遇到错误(除 JVMTI_ERROR_NONE
以外的任何返回值)参数指针引用的内存值未定义,但不会分配任何内存,也不会分配任何全局引用。如果由于输入无效而发生错误,则不会发生任何操作。
管理 JNI 对象引用
JVM TI 函数使用 JNI 引用(jobject
和 jclass
)及其派生物(jthread
和 jthreadGroup
)识别对象。传递给 JVM 的引用 TI 函数可以是全局的也可以是局部的,但必须是强引用。 JVM 返回的所有引用 TI 函数是本地引用——这些本地引用是在 JVM 期间创建的 德州仪器电话。本地引用是必须管理的资源(参见 JNI 文档 )。当线程从本机代码返回时,所有本地引用都将被释放。请注意,某些线程(包括典型的代理线程)永远不会从本机代码返回。确保线程能够创建十六个本地引用而无需任何显式管理。对于执行有限数量的 JVM 的线程 TI 在从本机代码返回之前调用(例如,线程处理事件),可以确定不需要显式管理。但是,长时间运行的代理线程将需要显式本地引用管理——通常使用 JNI 函数 PushLocalFrame
和 PopLocalFrame
。相反,要保留本机代码返回之外的引用,必须将它们转换为全局引用。这些规则不适用于 jmethodID
和 jfieldID
,因为它们不是 jobject
s。
调用函数的先决条件
除非函数明确声明代理必须将线程或 VM 置于特定状态(例如,挂起),否则 JVM TI 实现负责使 VM 进入安全且一致的状态以执行该功能。
异常和函数
JVM TI 函数从不抛出异常;错误情况通过 函数返回值 传达。在对 JVM 的调用中保留任何现有的异常状态 TI 功能。有关处理异常的信息,请参阅 JNI 规范的 Java 异常 部分。
功能索引
内存管理
内存管理功能:
这些函数提供 JVM 使用的内存的分配和释放 TI 功能,可用于为代理提供工作记忆。 JVM 管理的内存 TI 与其他内存分配库和机制不兼容。
分配
jvmtiError
Allocate(jvmtiEnv* env,
jlong size,
unsigned char** mem_ptr)
通过JVM分配一块内存 TI 分配器。应使用 Deallocate
释放分配的内存。
参数
Name |
Type |
Description |
size |
jlong |
要分配的字节数。 理由:jlong 用于与 JVMDI 兼容。 |
mem_ptr |
unsigned char** |
返回时,指向分配内存开头的指针。如果 size 为零,则返回 NULL 。
Agent 传递一个指向 unsigned char* 的指针。返回时,unsigned char* 指向新分配的大小为 size 的数组。该数组应使用 Deallocate 释放。 |
解除分配
jvmtiError
Deallocate(jvmtiEnv* env,
unsigned char* mem)
使用 JVM 释放 mem
TI 分配器。此函数应用于释放由 JVM 分配和返回的任何内存 TI 函数(包括分配给 Allocate
的内存)。必须释放所有分配的内存,否则无法回收内存。
参数
Name |
Type |
Description |
mem |
unsigned char * |
指向已分配内存开头的指针。请忽略“返回时,元素已设置”。
代理传递一个 unsigned char 数组。忽略数组元素的传入值。返回时,元素已设置。如果 mem 是 NULL ,调用将被忽略。 |
线
线程函数:
线程函数类型:
线程类型:
线程标志和常量:
这些函数提供有关线程的信息并允许代理挂起和恢复线程。
指定给这些函数的 jthread
可以是对 平台线程 或 虚拟线程 的 JNI 引用。某些函数在虚拟线程上不受支持,并且在使用对虚拟线程的引用调用时返回 JVMTI_ERROR_UNSUPPORTED_OPERATION
。
获取线程状态
jvmtiError
GetThreadState(jvmtiEnv* env,
jthread thread,
jint* thread_state_ptr)
获取线程的状态。线程的状态由以下分层问题集的答案表示:
答案由以下位向量表示。
线程状态标志
持续的 |
Value |
Description |
JVMTI_THREAD_STATE_ALIVE |
0x0001 |
线程还活着。如果线程是新的(未启动)或已终止则为零。 |
JVMTI_THREAD_STATE_TERMINATED |
0x0002 |
线程已完成执行。 |
JVMTI_THREAD_STATE_RUNNABLE |
0x0004 |
线程可运行。 |
JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER |
0x0400 |
线程正在等待进入同步块/方法,或者在 Object.wait() 之后等待重新进入同步块/方法。 |
JVMTI_THREAD_STATE_WAITING |
0x0080 |
线程正在等待。 |
JVMTI_THREAD_STATE_WAITING_INDEFINITELY |
0x0010 |
线程正在等待,没有超时。例如,Object.wait() 。 |
JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT |
0x0020 |
线程正在等待指定的最长时间等待。例如,Object.wait(long) 。 |
JVMTI_THREAD_STATE_SLEEPING |
0x0040 |
线程正在休眠 - Thread.sleep 。 |
JVMTI_THREAD_STATE_IN_OBJECT_WAIT |
0x0100 |
线程正在等待对象监视器 - Object.wait 。 |
JVMTI_THREAD_STATE_PARKED |
0x0200 |
线程已停放,例如:LockSupport.park 、LockSupport.parkUtil 和 LockSupport.parkNanos 。在 Thread.sleep 中处于休眠状态的虚拟线程可能会设置此状态标志而不是 JVMTI_THREAD_STATE_SLEEPING 。 |
JVMTI_THREAD_STATE_SUSPENDED |
0x100000 |
线程被挂起函数挂起(例如 SuspendThread )。如果设置了该位,则其他位指的是挂起前的线程状态。 |
JVMTI_THREAD_STATE_INTERRUPTED |
0x200000 |
线程已被中断。 |
JVMTI_THREAD_STATE_IN_NATIVE |
0x400000 |
线程在本机代码中——也就是说,一个本机方法正在运行,它没有回调到 VM 或 Java 编程语言代码中。
在运行 VM 编译的 Java 编程语言代码时不设置此标志,在运行 VM 代码或 VM 支持代码时也不设置。本地 VM 接口函数,例如 JNI 和 JVM TI 功能,可以实现为 VM 代码。 |
JVMTI_THREAD_STATE_VENDOR_1 |
0x10000000 |
由 VM 供应商定义。 |
JVMTI_THREAD_STATE_VENDOR_2 |
0x20000000 |
由 VM 供应商定义。 |
JVMTI_THREAD_STATE_VENDOR_3 |
0x40000000 |
由 VM 供应商定义。 |
以下定义用于转换JVM TI 线程状态到 java.lang.Thread.State
样式状态。
java.lang.Thread.State 转换掩码
持续的 |
Value |
Description |
JVMTI_JAVA_LANG_THREAD_STATE_MASK |
JVMTI_THREAD_STATE_TERMINATED | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT |
在比较之前用这个掩盖状态 |
JVMTI_JAVA_LANG_THREAD_STATE_NEW |
0 |
java.lang.Thread.State.NEW |
JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED |
JVMTI_THREAD_STATE_TERMINATED |
java.lang.Thread.State.TERMINATED |
JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE |
JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE |
java.lang.Thread.State.RUNNABLE |
JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED |
JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER |
java.lang.Thread.State.BLOCKED |
JVMTI_JAVA_LANG_THREAD_STATE_WAITING |
JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY |
java.lang.Thread.State.WAITING |
JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING |
JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT |
java.lang.Thread.State.TIMED_WAITING |
规则
一个问题只能有一个答案,尽管可以没有答案(因为答案未知,不适用,或者没有一个答案是正确的)。只有当封闭的答案匹配时才会设置答案。也就是说,不超过一个
JVMTI_THREAD_STATE_RUNNABLE
JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER
JVMTI_THREAD_STATE_WAITING
可以设置(一个J2SETM如果设置了 JVMTI_THREAD_STATE_ALIVE
,兼容的实现将始终设置其中之一)。如果设置了其中任何一个,则设置封闭的答案 JVMTI_THREAD_STATE_ALIVE
。不超过其中之一
JVMTI_THREAD_STATE_WAITING_INDEFINITELY
JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT
可以设置(一个J2SETM如果设置了 JVMTI_THREAD_STATE_WAITING
,兼容的实现将始终设置其中之一)。如果设置了其中一个,则设置了封闭的答案 JVMTI_THREAD_STATE_ALIVE
和 JVMTI_THREAD_STATE_WAITING
。不超过其中之一
JVMTI_THREAD_STATE_IN_OBJECT_WAIT
JVMTI_THREAD_STATE_PARKED
JVMTI_THREAD_STATE_SLEEPING
可以设置。如果设置了其中任何一个,则设置封闭的答案 JVMTI_THREAD_STATE_ALIVE
和 JVMTI_THREAD_STATE_WAITING
。此外,如果设置了 JVMTI_THREAD_STATE_SLEEPING
,则设置了 JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT
。如果一个状态A使用状态机制实现B然后是状态A这是由这个函数返回的。例如,如果 Thread.sleep(long)
是使用 Object.wait(long)
实现的,那么返回的仍然是 JVMTI_THREAD_STATE_SLEEPING
。不止一个
JVMTI_THREAD_STATE_SUSPENDED
JVMTI_THREAD_STATE_INTERRUPTED
JVMTI_THREAD_STATE_IN_NATIVE
可以设置,但如果设置了任何一个,JVMTI_THREAD_STATE_ALIVE
也会被设置。
最后,除非未设置 JVMTI_THREAD_STATE_ALIVE
,否则无法设置 JVMTI_THREAD_STATE_TERMINATED
。
线程状态表示是为规范的未来版本中的扩展而设计的;应相应地使用线程状态值,即它们不应用作序数。大多数查询都可以通过测试单个位来进行,如果需要在 switch 语句中使用,则应该用感兴趣的位来屏蔽状态位。上面未定义的所有位都保留供将来使用。符合当前规范的 VM 必须将保留位设置为零。代理应该忽略保留位——它们不应该被假定为零,因此不应该被包含在比较中。
示例
请注意,以下值不包括保留位和供应商位。
在 synchronized
语句处阻塞的线程状态为:
JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER
尚未启动的线程的状态为:
0
Object.wait(3000)
处的线程状态为:
JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_WAITING +
JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
JVMTI_THREAD_STATE_MONITOR_WAITING
可运行时挂起的线程的状态为:
JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_RUNNABLE + JVMTI_THREAD_STATE_SUSPENDED
测试状态
在大多数情况下,可以通过测试与该问题对应的一位来确定线程状态。例如,测试线程是否正在休眠的代码:
jint state;
jvmtiError err;
err = (*jvmti)->GetThreadState(jvmti, thread, &state);
if (err == JVMTI_ERROR_NONE) {
if (state & JVMTI_THREAD_STATE_SLEEPING) { ...
对于等待(即,在 Object.wait
、停放或睡觉),它将是:
if (state & JVMTI_THREAD_STATE_WAITING) { ...
对于某些状态,需要测试不止一位,就像测试线程是否尚未启动时的情况一样:
if ((state & (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_TERMINATED)) == 0) { ...
区分定时和不定时 Object.wait
:
if (state & JVMTI_THREAD_STATE_IN_OBJECT_WAIT) {
if (state & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) {
printf("in Object.wait(long timeout)\n");
} else {
printf("in Object.wait()\n");
}
}
与java.lang.Thread.State
的关系
从 java.lang.Thread.getState()
返回的 java.lang.Thread.State
表示的线程状态是从该函数返回的信息的子集。可以使用提供的转换掩码确定相应的java.lang.Thread.State
。例如,这将返回 java.lang.Thread.State
线程状态的名称:
err = (*jvmti)->GetThreadState(jvmti, thread, &state);
abortOnError(err);
switch (state & JVMTI_JAVA_LANG_THREAD_STATE_MASK) {
case JVMTI_JAVA_LANG_THREAD_STATE_NEW:
return "NEW";
case JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED:
return "TERMINATED";
case JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE:
return "RUNNABLE";
case JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED:
return "BLOCKED";
case JVMTI_JAVA_LANG_THREAD_STATE_WAITING:
return "WAITING";
case JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING:
return "TIMED_WAITING";
}
参数
Name |
Type |
Description |
thread |
jthread |
要查询的线程。如果 thread 是 NULL ,则使用当前线程。 |
thread_state_ptr |
jint* |
返回时,指向由 线程状态标志 定义的状态标志。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
获取当前线程
jvmtiError
GetCurrentThread(jvmtiEnv* env,
jthread* thread_ptr)
获取当前线程。当前线程是调用函数的Java编程语言线程。如果 can_generate_early_vmstart
功能已启用且 java.lang.Thread
类尚未初始化,则该函数可能会在启动阶段返回 NULL
。
请注意,大多数 JVM 将线程作为参数的 TI 函数将接受 NULL
来表示当前线程。
参数
Name |
Type |
Description |
thread_ptr |
jthread* |
返回时,指向当前线程或 NULL 。
Agent 传递一个指向 jthread 的指针。返回时,jthread 已设置。 thread_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
获取所有线程
jvmtiError
GetAllThreads(jvmtiEnv* env,
jint* threads_count_ptr,
jthread** threads_ptr)
获取附加到 VM 的所有实时平台线程。线程列表包括 代理线程 。它不包括虚拟线程。如果 java.lang.Thread.isAlive()
将返回 true
,则线程处于活动状态,也就是说,线程已启动且尚未终止。线程的范围由 JVM 的上下文决定 TI 环境,通常是附加到 VM 的所有线程。
参数
Name |
Type |
Description |
threads_count_ptr |
jint* |
返回时,指向线程数。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
threads_ptr |
jthread** |
返回时,指向一组引用,每个线程一个。
Agent 传递一个指向 jthread* 的指针。返回时,jthread* 指向新分配的大小为 *threads_count_ptr 的数组。该数组应使用 Deallocate 释放。 threads_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
挂起线程
jvmtiError
SuspendThread(jvmtiEnv* env,
jthread thread)
挂起指定的线程。如果指定了调用线程,则此函数将不会返回,直到其他某个线程调用 ResumeThread
。如果线程当前挂起,则此函数不执行任何操作并返回错误。
参数
Name |
Type |
Description |
thread |
jthread |
要挂起的线程。如果 thread 是 NULL ,则使用当前线程。 |
挂起线程列表
jvmtiError
SuspendThreadList(jvmtiEnv* env,
jint request_count,
const jthread* request_list,
jvmtiError* results)
挂起 request_list
数组中指定的 request_count
个线程。可以使用 ResumeThreadList
或 ResumeThread
恢复线程。如果在 request_list
数组中指定了调用线程,则此函数将不会返回,直到某个其他线程恢复它。线程挂起遇到的错误在results
数组中返回,不是在这个函数的返回值中。当前挂起的线程不会改变状态。
参数
Name |
Type |
Description |
request_count |
jint |
要挂起的线程数。 |
request_list |
const jthread* |
要挂起的线程列表。
代理传入 jthread 的 request_count 个元素的数组。 |
results |
jvmtiError* |
代理提供了 request_count 个元素的数组。返回时,填充相应线程挂起的错误代码。如果线程被此调用挂起,错误代码将为 JVMTI_ERROR_NONE 。可能的错误代码是为 SuspendThread 指定的代码。
Agent 传递一个足够大的数组来容纳 jvmtiError 的 request_count 个元素。忽略数组元素的传入值。返回时,元素已设置。 |
挂起所有虚拟线程
jvmtiError
SuspendAllVirtualThreads(jvmtiEnv* env,
jint except_count,
const jthread* except_list)
SuspendAllVirtualThreads 是 Java 平台的预览 API。 预览功能可能会在未来的版本中删除,或升级为 Java 平台的永久功能。
挂起除异常列表中的虚拟线程之外的所有虚拟线程。当前挂起的虚拟线程不会改变状态。可以使用 ResumeAllVirtualThreads
或 ResumeThreadList
或 ResumeThread
恢复虚拟线程。
参数
Name |
Type |
Description |
except_count |
jint |
线程列表中不被挂起的线程数。 |
except_list |
const jthread * |
不被挂起的线程列表。
代理传入 jthread 的 except_count 个元素的数组。如果 except_list 是 NULL ,如果是 except_count == 0 则不是错误。 |
恢复线程
jvmtiError
ResumeThread(jvmtiEnv* env,
jthread thread)
恢复挂起的线程。当前通过 JVM 挂起的任何线程 TI 暂停功能(例如 SuspendThread
)将恢复执行;所有其他线程不受影响。
参数
Name |
Type |
Description |
thread |
jthread |
要恢复的线程。 |
恢复线程列表
jvmtiError
ResumeThreadList(jvmtiEnv* env,
jint request_count,
const jthread* request_list,
jvmtiError* results)
恢复 request_list
数组中指定的 request_count
个线程。通过 JVM 挂起的任何线程 TI 暂停功能(例如 SuspendThreadList
)将恢复执行。
参数
Name |
Type |
Description |
request_count |
jint |
要恢复的线程数。 |
request_list |
const jthread* |
要恢复的线程。
代理传入 jthread 的 request_count 个元素的数组。 |
results |
jvmtiError* |
代理提供的 request_count 元素数组。返回时,填充相应线程恢复的错误代码。如果线程被此调用挂起,错误代码将为 JVMTI_ERROR_NONE 。可能的错误代码是为 ResumeThread 指定的代码。
Agent 传递一个足够大的数组来容纳 jvmtiError 的 request_count 个元素。忽略数组元素的传入值。返回时,元素已设置。 |
恢复所有虚拟线程
jvmtiError
ResumeAllVirtualThreads(jvmtiEnv* env,
jint except_count,
const jthread* except_list)
ResumeAllVirtualThreads 是 Java 平台的预览 API。 预览功能可能会在未来的版本中删除,或升级为 Java 平台的永久功能。
恢复除异常列表中的虚拟线程之外的所有虚拟线程。当前恢复的虚拟线程不会改变状态。可以使用 SuspendAllVirtualThreads
或 SuspendThreadList
或 SuspendThread
暂停虚拟线程。
参数
Name |
Type |
Description |
except_count |
jint |
线程列表中不被恢复的线程数。 |
except_list |
const jthread * |
不恢复的线程列表。
代理传入 jthread 的 except_count 个元素的数组。如果 except_list 是 NULL ,如果是 except_count == 0 则不是错误。 |
停止线程
jvmtiError
StopThread(jvmtiEnv* env,
jthread thread,
jobject exception)
将指定的异步异常发送到指定的平台线程。
中断线程
jvmtiError
InterruptThread(jvmtiEnv* env,
jthread thread)
中断指定的线程(类似于 java.lang.Thread.interrupt
)。
参数
Name |
Type |
Description |
thread |
jthread |
要中断的线程。 |
获取线程信息
typedef struct {
char* name;
jint priority;
jboolean is_daemon;
jthreadGroup thread_group;
jobject context_class_loader;
} jvmtiThreadInfo;
jvmtiError
GetThreadInfo(jvmtiEnv* env,
jthread thread,
jvmtiThreadInfo* info_ptr)
获取线程信息。 jvmtiThreadInfo
结构的字段填充了指定线程的详细信息。
jvmtiThreadInfo
- 线程信息结构
Field |
Type |
Description |
name |
char* |
线程名称,编码为 修改后的 UTF-8 字符串。 |
priority |
jint |
线程优先级。请参阅线程优先级常量:jvmtiThreadPriority 。虚拟线程的优先级始终是 JVMTI_THREAD_NORM_PRIORITY 。 |
is_daemon |
jboolean |
这是守护线程吗?虚拟线程的守护进程状态始终为 JNI_TRUE 。 |
thread_group |
jthreadGroup |
该线程所属的线程组。 NULL 如果线程已终止。 |
context_class_loader |
jobject |
与此线程关联的上下文类加载器。 |
参数
Name |
Type |
Description |
thread |
jthread |
要查询的线程。如果 thread 是 NULL ,则使用当前线程。 |
info_ptr |
jvmtiThreadInfo* |
返回时,填充了描述指定线程的信息。
Agent 传递一个指向 jvmtiThreadInfo 的指针。返回时,jvmtiThreadInfo 已设置。 jvmtiThreadInfo 的name 字段中返回的指针是一个新分配的数组。该数组应使用 Deallocate 释放。 jvmtiThreadInfo 的 thread_group 字段中返回的对象是 JNI 本地引用,必须是 管理。 jvmtiThreadInfo 的字段 context_class_loader 中返回的对象是 JNI 本地引用,必须是 管理。 |
获取拥有的显示器信息
jvmtiError
GetOwnedMonitorInfo(jvmtiEnv* env,
jthread thread,
jint* owned_monitor_count_ptr,
jobject** owned_monitors_ptr)
获取有关指定线程拥有的监视器的信息。
参数
Name |
Type |
Description |
thread |
jthread |
要查询的线程。如果 thread 是 NULL ,则使用当前线程。 |
owned_monitor_count_ptr |
jint* |
返回的监视器数。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
owned_monitors_ptr |
jobject** |
拥有的监视器数组。
Agent 传递一个指向 jobject* 的指针。返回时,jobject* 指向新分配的大小为 *owned_monitor_count_ptr 的数组。该数组应使用 Deallocate 释放。 owned_monitors_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
获取拥有的监视器堆栈深度信息
typedef struct {
jobject monitor;
jint stack_depth;
} jvmtiMonitorStackDepthInfo;
jvmtiError
GetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
jthread thread,
jint* monitor_info_count_ptr,
jvmtiMonitorStackDepthInfo** monitor_info_ptr)
获取有关指定线程拥有的监视器以及锁定它们的堆栈帧深度的信息。
jvmtiMonitorStackDepthInfo
- 监控堆栈深度信息结构
Field |
Type |
Description |
monitor |
jobject |
自有显示器。 |
stack_depth |
jint |
堆栈深度。对应于 栈帧函数 中使用的堆栈深度。即零为当前帧,一为调用当前帧的帧。如果实现无法确定堆栈深度(例如,对于 JNI MonitorEnter 获取的监视器),则为负数。 |
参数
Name |
Type |
Description |
thread |
jthread |
要查询的线程。如果 thread 是 NULL ,则使用当前线程。 |
monitor_info_count_ptr |
jint* |
返回的监视器数。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
monitor_info_ptr |
jvmtiMonitorStackDepthInfo ** |
拥有的监视器深度信息的数组。
Agent 传递一个指向 jvmtiMonitorStackDepthInfo* 的指针。返回时,jvmtiMonitorStackDepthInfo* 指向新分配的大小为 *monitor_info_count_ptr 的数组。该数组应使用 Deallocate 释放。 jvmtiMonitorStackDepthInfo 的 monitor 字段中返回的对象是 JNI 本地引用,必须是 管理。 |
获取当前竞争监视器
jvmtiError
GetCurrentContendedMonitor(jvmtiEnv* env,
jthread thread,
jobject* monitor_ptr)
通过 java.lang.Object.wait
获取指定线程正在等待进入或等待重新获得其监视器的对象(如果有)。
参数
Name |
Type |
Description |
thread |
jthread |
要查询的线程。如果 thread 是 NULL ,则使用当前线程。 |
monitor_ptr |
jobject* |
返回时,填充当前竞争的监视器,如果没有则为 NULL。
代理传递一个指向 jobject 的指针。返回时,jobject 已设置。 monitor_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
代理启动函数
typedef void (JNICALL *jvmtiStartFunction)
(jvmtiEnv* jvmti_env,
JNIEnv* jni_env,
void* arg);
代理提供的回调函数。此函数是以
RunAgentThread
开头的代理线程的入口点。
运行代理线程
jvmtiError
RunAgentThread(jvmtiEnv* env,
jthread thread,
jvmtiStartFunction proc,
const void* arg,
jint priority)
开始执行代理线程。具有指定的本机功能。参数 arg
作为其单个参数转发给 开始功能(用 proc
指定)。此函数允许创建代理线程来处理与另一个进程的通信或处理事件,而无需加载 java.lang.Thread
的特殊子类或 java.lang.Runnable
的实现者。相反,创建的线程可以完全以本机代码运行。但是,创建的线程确实需要一个新创建的 java.lang.Thread
实例(由参数 thread
引用),它将与之关联。可以使用 JNI 调用创建线程对象。
为方便起见,提供了以下公共线程优先级:
线程优先级常量
持续的 |
Value |
Description |
JVMTI_THREAD_MIN_PRIORITY |
1 |
最低可能的线程优先级 |
JVMTI_THREAD_NORM_PRIORITY |
5 |
普通线程优先级 |
JVMTI_THREAD_MAX_PRIORITY |
10 |
最大可能的线程优先级 |
新线程作为具有指定 priority
的守护线程启动。如果启用,将发送 ThreadStart
事件。
由于线程已经启动,除非线程立即终止,否则当此函数返回时线程将处于活动状态。
线程的线程组被忽略——具体来说,线程没有添加到线程组中,并且在 Java 编程语言或 JVM 的线程组查询中都看不到线程 TI 级别。
该线程对 Java 编程语言查询不可见,但包含在 JVM 中 TI 查询(例如,GetAllThreads
和 GetAllStackTraces
)。
执行 proc
后,新线程将附加到 VM - 请参阅 附加到虚拟机 上的 JNI 文档。
设置线程本地存储
jvmtiError
SetThreadLocalStorage(jvmtiEnv* env,
jthread thread,
const void* data)
VM 存储与每个环境线程对关联的指针值。这个指针值被称为线程本地存储.这个值是NULL
除非用这个函数设置。代理可以分配存储线程特定信息的内存。通过设置线程本地存储,然后可以使用 GetThreadLocalStorage
访问它。
此函数由代理调用以设置 JVM 的值 TI 线程本地存储。虚拟机 TI 向代理提供指针大小的线程本地存储,可用于记录每个线程的信息。
参数
Name |
Type |
Description |
thread |
jthread |
存储到此线程。如果 thread 是 NULL ,则使用当前线程。 |
data |
const void * |
要输入到线程本地存储中的值。
Agent 传入一个指针。如果 data 为 NULL ,则值设置为 NULL 。 |
获取线程本地存储
jvmtiError
GetThreadLocalStorage(jvmtiEnv* env,
jthread thread,
void** data_ptr)
被agent调用获取JVM的值 TI 线程本地存储。
参数
Name |
Type |
Description |
thread |
jthread |
从此线程中检索。如果 thread 是 NULL ,则使用当前线程。 |
data_ptr |
void** |
返回线程本地存储值的指针。如果未使用 SetThreadLocalStorage 设置线程本地存储,则返回的指针为 NULL 。
|
线程组
线程组函数:
线程组类型:
获取顶级线程组
jvmtiError
GetTopThreadGroups(jvmtiEnv* env,
jint* group_count_ptr,
jthreadGroup** groups_ptr)
返回 VM 中的所有顶级(无父级)线程组。
参数
Name |
Type |
Description |
group_count_ptr |
jint* |
返回时,指向顶级线程组的数量。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
groups_ptr |
jthreadGroup** |
返回时,指的是指向顶级线程组数组的指针。
Agent 传递一个指向 jthreadGroup* 的指针。返回时,jthreadGroup* 指向新分配的大小为 *group_count_ptr 的数组。该数组应使用 Deallocate 释放。 groups_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
获取线程组信息
typedef struct {
jthreadGroup parent;
char* name;
jint max_priority;
jboolean is_daemon;
} jvmtiThreadGroupInfo;
jvmtiError
GetThreadGroupInfo(jvmtiEnv* env,
jthreadGroup group,
jvmtiThreadGroupInfo* info_ptr)
获取有关线程组的信息。 jvmtiThreadGroupInfo
结构的字段填充了指定线程组的详细信息。
jvmtiThreadGroupInfo
- 线程组信息结构
Field |
Type |
Description |
parent |
jthreadGroup |
父线程组。 |
name |
char* |
线程组的名称,编码为 修改后的 UTF-8 字符串。 |
max_priority |
jint |
此线程组的最大优先级。 |
is_daemon |
jboolean |
这是守护进程线程组吗? |
参数
Name |
Type |
Description |
group |
jthreadGroup |
要查询的线程组。 |
info_ptr |
jvmtiThreadGroupInfo* |
返回时,填充了描述指定线程组的信息。
Agent 传递一个指向 jvmtiThreadGroupInfo 的指针。返回时,jvmtiThreadGroupInfo 已设置。 jvmtiThreadGroupInfo 的字段 parent 中返回的对象是 JNI 本地引用,必须是 管理。 jvmtiThreadGroupInfo 的name 字段中返回的指针是一个新分配的数组。该数组应使用 Deallocate 释放。 |
获取线程组子项
jvmtiError
GetThreadGroupChildren(jvmtiEnv* env,
jthreadGroup group,
jint* thread_count_ptr,
jthread** threads_ptr,
jint* group_count_ptr,
jthreadGroup** groups_ptr)
获取直播平台线程和该线程组中的子线程组。此函数不返回虚拟线程。
参数
Name |
Type |
Description |
group |
jthreadGroup |
要查询的组。 |
thread_count_ptr |
jint* |
返回时,指向此线程组中的活动线程数。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
threads_ptr |
jthread** |
返回时,指向该线程组中活动线程的数组。
Agent 传递一个指向 jthread* 的指针。返回时,jthread* 指向新分配的大小为 *thread_count_ptr 的数组。该数组应使用 Deallocate 释放。 threads_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
group_count_ptr |
jint* |
返回时,指向子线程组的数量
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
groups_ptr |
jthreadGroup** |
返回时,指向子线程组的数组。
Agent 传递一个指向 jthreadGroup* 的指针。返回时,jthreadGroup* 指向新分配的大小为 *group_count_ptr 的数组。该数组应使用 Deallocate 释放。 groups_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
栈帧
栈帧函数:
栈帧类型:
这些函数提供有关线程堆栈的信息。堆栈帧按深度引用。深度为零的帧是当前帧。
堆栈帧如中所述Java™ 虚拟机规范,第 3.6 章,也就是说,它们对应于方法调用(包括原生方法),但不对应于平台原生或VM内部框架。
A JVM TI 实现可能会使用方法调用来启动线程,并且相应的帧可能会包含在这些函数所呈现的堆栈中——也就是说,可能会显示比 main()
和 run()
更深的帧。然而,这种表示必须在所有 JVM 中保持一致 使用堆栈帧或堆栈深度的 TI 功能。
栈帧信息结构
在此结构中返回有关堆栈帧的信息。
typedef struct {
jmethodID method;
jlocation location;
} jvmtiFrameInfo;
jvmtiFrameInfo
- 栈帧信息结构
Field |
Type |
Description |
method |
jmethodID |
在此框架中执行的方法。 |
location |
jlocation |
在此帧中执行的指令的索引。 -1 如果框架正在执行本地方法。 |
堆栈信息结构
在此结构中返回有关一组堆栈帧的信息。
typedef struct {
jthread thread;
jint state;
jvmtiFrameInfo* frame_buffer;
jint frame_count;
} jvmtiStackInfo;
jvmtiStackInfo
- 堆栈信息结构
Field |
Type |
Description |
thread |
jthread |
返回时,跟踪线程。 |
state |
jint |
返回时,线程状态。参见 GetThreadState 。 |
frame_buffer |
jvmtiFrameInfo * |
返回时,此代理分配的缓冲区将填充堆栈帧信息。 |
frame_count |
jint |
返回时,填入 frame_buffer 的记录数。这将是 min(max_frame_count ,堆栈深度). |
获取堆栈跟踪
jvmtiError
GetStackTrace(jvmtiEnv* env,
jthread thread,
jint start_depth,
jint max_frame_count,
jvmtiFrameInfo* frame_buffer,
jint* count_ptr)
获取有关线程堆栈的信息。如果 max_frame_count
小于堆栈深度,则返回 max_frame_count
最顶层帧,否则返回整个堆栈。最顶层的帧,即最近调用的帧,位于返回缓冲区的开头。
以下示例导致返回最多五个最顶层的帧,并且(如果有任何帧)打印当前正在执行的方法名称。
jvmtiFrameInfo frames[5];
jint count;
jvmtiError err;
err = (*jvmti)->GetStackTrace(jvmti, aThread, 0, 5,
frames, &count);
if (err == JVMTI_ERROR_NONE && count >= 1) {
char *methodName;
err = (*jvmti)->GetMethodName(jvmti, frames[0].method,
&methodName, NULL, NULL);
if (err == JVMTI_ERROR_NONE) {
printf("Executing method: %s", methodName);
}
}
thread
无需暂停即可调用此函数。
GetLineNumberTable
函数可用于将位置映射到行号。请注意,此map可以延迟完成。
参数
Name |
Type |
Description |
thread |
jthread |
获取此线程的堆栈跟踪。如果 thread 是 NULL ,则使用当前线程。 |
start_depth |
jint |
在此深度开始检索帧。如果非负,则从当前帧开始计数,检索到的第一帧深度为 start_depth 。例如,如果为零,则从当前帧开始;如果有,则从当前帧的调用者开始;如果是两个,则从当前帧的调用者的调用者开始;等等。如果为负数,则从最旧的帧以下开始计数,检索到的第一帧在深度堆栈深度 + start_depth ,其中堆栈深度是堆栈上的帧数。例如,如果为负数,则只检索最旧的帧;如果为负二,则从最旧的帧调用的帧开始。 |
max_frame_count |
jint |
要检索的最大 jvmtiFrameInfo 记录数。 |
frame_buffer |
jvmtiFrameInfo * |
返回时,此代理分配的缓冲区将填充堆栈帧信息。
Agent 传递一个大到足以容纳 jvmtiFrameInfo 的 max_frame_count 个元素的数组。忽略数组元素的传入值。返回时,设置了 *count_ptr 个元素。 |
count_ptr |
jint* |
返回时,指向填写的记录数。对于非负 start_depth ,这将是 min(max_frame_count ,堆栈深度 - start_depth )。对于负数 start_depth ,这将是 min(max_frame_count , -start_depth )。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
获取所有堆栈跟踪
jvmtiError
GetAllStackTraces(jvmtiEnv* env,
jint max_frame_count,
jvmtiStackInfo** stack_info_ptr,
jint* thread_count_ptr)
获取附加到 VM 的所有实时平台线程的堆栈跟踪。该列表包括 代理线程 的堆栈跟踪。它不包括虚拟线程的堆栈跟踪。
如果 max_frame_count
小于堆栈深度,则为该线程返回 max_frame_count
最顶层帧,否则返回整个堆栈。最顶层的帧,即最近调用的帧,位于返回缓冲区的开头。
同时收集所有堆栈,也就是说,在一个线程和下一个线程的采样之间,线程状态或堆栈不会发生任何变化。不需要挂起线程。
jvmtiStackInfo *stack_info;
jint thread_count;
int ti;
jvmtiError err;
err = (*jvmti)->GetAllStackTraces(jvmti, MAX_FRAMES, &stack_info, &thread_count);
if (err != JVMTI_ERROR_NONE) {
...
}
for (ti = 0; ti < thread_count; ++ti) {
jvmtiStackInfo *infop = &stack_info[ti];
jthread thread = infop->thread;
jint state = infop->state;
jvmtiFrameInfo *frames = infop->frame_buffer;
int fi;
myThreadAndStatePrinter(thread, state);
for (fi = 0; fi < infop->frame_count; fi++) {
myFramePrinter(frames[fi].method, frames[fi].location);
}
}
/* this one Deallocate call frees all data allocated by GetAllStackTraces */
err = (*jvmti)->Deallocate(jvmti, stack_info);
获取线程列表堆栈跟踪
jvmtiError
GetThreadListStackTraces(jvmtiEnv* env,
jint thread_count,
const jthread* thread_list,
jint max_frame_count,
jvmtiStackInfo** stack_info_ptr)
获取有关所提供线程堆栈的信息。如果 max_frame_count
小于堆栈深度,则为该线程返回 max_frame_count
最顶层帧,否则返回整个堆栈。最顶层的帧,即最近调用的帧,位于返回缓冲区的开头。
同时收集所有堆栈,也就是说,在采样一个线程和下一个线程之间,线程状态或堆栈不会发生任何变化。不需要挂起线程。
如果在收集堆栈信息之前线程尚未启动或终止,则将返回零长度堆栈(jvmtiStackInfo.frame_count
将为零)并且可以检查线程 jvmtiStackInfo.state
。
请参阅类似函数 GetAllStackTraces
的示例。
获取帧数
jvmtiError
GetFrameCount(jvmtiEnv* env,
jthread thread,
jint* count_ptr)
获取当前在指定线程的调用堆栈中的帧数。
如果为主动执行字节码的线程调用此函数(例如,不是当前线程且未挂起),则返回的信息是瞬态的。
参数
Name |
Type |
Description |
thread |
jthread |
要查询的线程。如果 thread 是 NULL ,则使用当前线程。 |
count_ptr |
jint* |
返回时,指向调用堆栈中的帧数。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
流行框架
jvmtiError
PopFrame(jvmtiEnv* env,
jthread thread)
弹出 thread
堆栈的当前帧。弹出一帧会将您带到上一帧。当线程恢复时,线程的执行状态将重置为调用被调用方法之前的状态。那就是(使用Java™ 虚拟机规范术语):
- 当前帧被丢弃,因为前一帧成为当前帧
- 操作数堆栈被恢复——参数值被加回来,如果调用不是
invokestatic
, objectref
也被加回来
- Java虚拟机PC还原为invoke指令的操作码
但是请注意,在被调用方法中发生的对参数的任何更改都将保留;当执行继续时,要执行的第一条指令将是调用。
在调用 PopFrame
和恢复线程之间,堆栈的状态是未定义的。要弹出第一帧之后的帧,必须重复这三个步骤:
- 通过事件(步骤、断点、...)挂起线程
- 打电话给
PopFrame
- 恢复线程
通过调用被调用方法(如果是synchronized
方法)获得的锁和通过在被调用方法中进入synchronized
块获得的锁被释放。注意:这不适用于本机锁或 java.util.concurrent.locks
锁。
finally 块不被执行。
全局状态的变化没有得到解决,因此保持不变。
指定的线程必须挂起或者必须是当前线程。
被调用方法和调用方法都必须是非原生 Java 编程语言方法。
无虚拟机 TI 事件由此函数生成。
参数
Name |
Type |
Description |
thread |
jthread |
要弹出其当前帧的线程。 |
获取帧位置
jvmtiError
GetFrameLocation(jvmtiEnv* env,
jthread thread,
jint depth,
jmethodID* method_ptr,
jlocation* location_ptr)
对于 Java 编程语言框架,返回当前正在执行的指令的位置。
参数
Name |
Type |
Description |
thread |
jthread |
要查询的框架的线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
要查询的帧的深度。 |
method_ptr |
jmethodID* |
返回时,指向当前位置的方法。
Agent 传递一个指向 jmethodID 的指针。返回时,jmethodID 已设置。 |
location_ptr |
jlocation* |
返回时,指向当前执行指令的索引。如果框架正在执行本机方法,则设置为 -1 。
代理将指针传递给 jlocation 。返回时,jlocation 已设置。 |
通知帧弹出
jvmtiError
NotifyFramePop(jvmtiEnv* env,
jthread thread,
jint depth)
当当前位于 depth
的帧从堆栈中弹出时,生成一个 FramePop
事件。有关详细信息,请参阅 FramePop
事件。只有对应于非本机 Java 编程语言方法的框架才能接收通知。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
将为其生成框架弹出事件的框架的线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
将为其生成框架弹出事件的框架的深度。 |
强制提前返回
强制提前返回功能:
这些函数允许代理强制方法在其执行期间的任何时候返回。提前返回的方法称为被调用的方法.被调用的方法是当前方法(由Java™ 虚拟机规范,第 3.6 章) 在调用函数时针对指定的线程。
指定的线程必须挂起或者必须是当前线程。在此线程上恢复执行 Java 编程语言代码时发生返回。在调用这些函数之一和恢复线程执行之间,堆栈的状态是未定义的。
在被调用的方法中没有执行进一步的指令。具体来说,finally 块不会被执行。注意:这可能会导致应用程序中出现不一致的状态。
通过调用被调用方法(如果是synchronized
方法)获得的锁和通过在被调用方法中进入synchronized
块获得的锁被释放。注意:这不适用于本机锁或 java.util.concurrent.locks
锁。
事件(例如 MethodExit
)的生成与正常返回中的一样。
被调用的方法必须是非本机 Java 编程语言方法。在堆栈上只有一帧的线程上强制返回会导致线程在恢复时退出。
强制提前返回 - 对象
jvmtiError
ForceEarlyReturnObject(jvmtiEnv* env,
jthread thread,
jobject value)
此函数可用于从结果类型为 Object
或 Object
的子类的方法返回。
参数
Name |
Type |
Description |
thread |
jthread |
当前帧要提前返回的线程。如果 thread 是 NULL ,则使用当前线程。 |
value |
jobject |
被调用帧的返回值。一个对象或 NULL 。 |
强制提前返回 - Int
jvmtiError
ForceEarlyReturnInt(jvmtiEnv* env,
jthread thread,
jint value)
此函数可用于从结果类型为 int
、short
、char
、byte
或 boolean
的方法返回。
参数
Name |
Type |
Description |
thread |
jthread |
当前帧要提前返回的线程。如果 thread 是 NULL ,则使用当前线程。 |
value |
jint |
被调用帧的返回值。 |
强制提前返回 - 长
jvmtiError
ForceEarlyReturnLong(jvmtiEnv* env,
jthread thread,
jlong value)
此函数可用于从结果类型为 long
的方法返回。
参数
Name |
Type |
Description |
thread |
jthread |
当前帧要提前返回的线程。如果 thread 是 NULL ,则使用当前线程。 |
value |
jlong |
被调用帧的返回值。 |
强制提前返回 - 浮动
jvmtiError
ForceEarlyReturnFloat(jvmtiEnv* env,
jthread thread,
jfloat value)
此函数可用于从结果类型为 float
的方法返回。
参数
Name |
Type |
Description |
thread |
jthread |
当前帧要提前返回的线程。如果 thread 是 NULL ,则使用当前线程。 |
value |
jfloat |
被调用帧的返回值。 |
强制提前返回 - 双
jvmtiError
ForceEarlyReturnDouble(jvmtiEnv* env,
jthread thread,
jdouble value)
此函数可用于从结果类型为 double
的方法返回。
参数
Name |
Type |
Description |
thread |
jthread |
当前帧要提前返回的线程。如果 thread 是 NULL ,则使用当前线程。 |
value |
jdouble |
被调用帧的返回值。 |
强制提前返回 - 无效
jvmtiError
ForceEarlyReturnVoid(jvmtiEnv* env,
jthread thread)
此函数可用于从没有结果类型的方法返回。也就是说,调用的方法必须声明为 void
。
参数
Name |
Type |
Description |
thread |
jthread |
当前帧要提前返回的线程。如果 thread 是 NULL ,则使用当前线程。 |
堆
堆函数:
堆函数类型:
堆类型:
堆标志和常量:
这些函数用于分析堆。功能包括查看堆中的对象和标记这些对象的能力。
A tag是与对象关联的值。标签由代理使用 SetTag
函数或回调函数(如 jvmtiHeapIterationCallback
)显式设置。
标签是本地环境;也就是说,一个环境的标签在另一个环境中是不可见的。
标签是 jlong
值,可以简单地用于标记对象或存储指向更详细信息的指针。未标记的对象的标记为零。将标签设置为零会使对象不带标签。
堆回调函数
遍历堆并递归地遵循对象引用的堆函数使用代理提供的回调函数来传递信息。
这些堆回调函数必须遵守以下限制——这些回调不能使用 JNI 函数。这些回调不能使用 JVM TI 功能除回调安全专门允许此类使用的功能(请参阅原始监视器、内存管理和环境本地存储功能)。
实现可以在内部线程或调用迭代函数的线程上调用回调。堆回调是单线程的——一次不会调用多个回调。
堆过滤器标志可用于防止基于对象或其类的标记状态的报告。如果未设置标志(jint
为零),则不会过滤掉对象。
堆过滤器标志
持续的 |
Value |
Description |
JVMTI_HEAP_FILTER_TAGGED |
0x4 |
过滤掉标记的对象。不包括标记的对象。 |
JVMTI_HEAP_FILTER_UNTAGGED |
0x8 |
过滤掉未标记的对象。不包括未标记的对象。 |
JVMTI_HEAP_FILTER_CLASS_TAGGED |
0x10 |
过滤掉带有标记类的对象。不包括其类被标记的对象。 |
JVMTI_HEAP_FILTER_CLASS_UNTAGGED |
0x20 |
过滤掉具有未标记类的对象。不包括其类未标记的对象。 |
堆访问控制标志由堆回调返回,可用于中止迭代。对于 堆引用回调 ,它也可以用于修剪遍历引用的图形(JVMTI_VISIT_OBJECTS
未设置)。
堆访问控制标志
持续的 |
Value |
Description |
JVMTI_VISIT_OBJECTS |
0x100 |
如果我们正在访问一个对象,并且如果这个回调是由 FollowReferences 发起的,则遍历这个对象的引用。否则忽略。 |
JVMTI_VISIT_ABORT |
0x8000 |
中止迭代。忽略所有其他位。 |
堆引用枚举由 堆引用回调 和 原始字段回调 提供,用于描述所报告的引用类型。
堆引用枚举 (jvmtiHeapReferenceKind
)
持续的 |
Value |
Description |
JVMTI_HEAP_REFERENCE_CLASS |
1 |
从一个对象到它的类的引用。 |
JVMTI_HEAP_REFERENCE_FIELD |
2 |
从对象到其实例字段之一的值的引用。 |
JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT |
3 |
从数组到其元素之一的引用。 |
JVMTI_HEAP_REFERENCE_CLASS_LOADER |
4 |
从一个类到它的类加载器的引用。 |
JVMTI_HEAP_REFERENCE_SIGNERS |
5 |
从一个类到它的 signers 数组的引用。 |
JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN |
6 |
从一个类到它的保护域的引用。 |
JVMTI_HEAP_REFERENCE_INTERFACE |
7 |
从一个类到它的接口之一的引用。注意:接口是通过常量池引用定义的,因此引用的接口也可以使用 JVMTI_HEAP_REFERENCE_CONSTANT_POOL 引用类型进行报告。 |
JVMTI_HEAP_REFERENCE_STATIC_FIELD |
8 |
从一个类到它的静态字段之一的值的引用。 |
JVMTI_HEAP_REFERENCE_CONSTANT_POOL |
9 |
从类到常量池中已解析条目的引用。 |
JVMTI_HEAP_REFERENCE_SUPERCLASS |
10 |
从一个类到它的超类的引用。如果超类是 java.lang.Object 则不会发送回调。注意:加载的类通过常量池引用定义超类,因此引用的超类也可以使用 JVMTI_HEAP_REFERENCE_CONSTANT_POOL 引用类型进行报告。 |
JVMTI_HEAP_REFERENCE_JNI_GLOBAL |
21 |
堆根引用:JNI 全局引用。 |
JVMTI_HEAP_REFERENCE_SYSTEM_CLASS |
22 |
堆根引用:系统类。 |
JVMTI_HEAP_REFERENCE_MONITOR |
23 |
堆根引用:monitor。 |
JVMTI_HEAP_REFERENCE_STACK_LOCAL |
24 |
堆根引用:栈上的局部变量。 |
JVMTI_HEAP_REFERENCE_JNI_LOCAL |
25 |
堆根引用:JNI 本地引用。 |
JVMTI_HEAP_REFERENCE_THREAD |
26 |
堆根引用:线程。 |
JVMTI_HEAP_REFERENCE_OTHER |
27 |
堆根引用:其他堆根引用。 |
基本类型的单个字符类型描述符的定义。
原始类型枚举 (jvmtiPrimitiveType
)
持续的 |
Value |
Description |
JVMTI_PRIMITIVE_TYPE_BOOLEAN |
90 |
'Z' - Java 编程语言 boolean - JNI jboolean |
JVMTI_PRIMITIVE_TYPE_BYTE |
66 |
'B' - Java 编程语言 byte - JNI jbyte |
JVMTI_PRIMITIVE_TYPE_CHAR |
67 |
'C' - Java 编程语言 char - JNI jchar |
JVMTI_PRIMITIVE_TYPE_SHORT |
83 |
'S' - Java 编程语言 short - JNI jshort |
JVMTI_PRIMITIVE_TYPE_INT |
73 |
'I' - Java 编程语言 int - JNI jint |
JVMTI_PRIMITIVE_TYPE_LONG |
74 |
'J' - Java 编程语言 long - JNI jlong |
JVMTI_PRIMITIVE_TYPE_FLOAT |
70 |
'F' - Java 编程语言 float - JNI jfloat |
JVMTI_PRIMITIVE_TYPE_DOUBLE |
68 |
'D' - Java 编程语言 double - JNI jdouble |
字段引用的引用信息结构
为 JVMTI_HEAP_REFERENCE_FIELD
和 JVMTI_HEAP_REFERENCE_STATIC_FIELD
引用返回的引用信息。
typedef struct {
jint index;
} jvmtiHeapReferenceInfoField;
jvmtiHeapReferenceInfoField
- 字段引用的引用信息结构
Field |
Type |
Description |
index |
jint |
对于 JVMTI_HEAP_REFERENCE_FIELD ,引用对象不是类或接口。在这种情况下,index 是引用对象类中字段的索引。这个类在下面被称为C.
对于 JVMTI_HEAP_REFERENCE_STATIC_FIELD ,引用对象是一个类(以下称为C) 或接口(以下称为I).在这种情况下,index 是该类或接口中字段的索引。
如果 referrer 对象不是接口,则字段索引确定如下:
- 列出所有字段C及其超类,以
java.lang.Object 中的所有字段开始,以 java.lang.Object 中的所有字段结束C.
- 在此list中,按照
GetClassFields 返回的顺序放置给定类的字段。
- 分配此list索引中的字段n, n+1, ..., 按顺序,哪里n是实现的所有接口的字段数C.注意C实现由其超类直接实现的所有接口;以及这些接口的所有超接口。
如果 referrer 对象是接口,则字段索引确定如下:
- 列出直接声明的字段I.
- 在此list中,按照
GetClassFields 返回的顺序放置字段。
- 分配此list索引中的字段n, n+1, ..., 按顺序,哪里n是所有超接口中字段的计数I.
所有字段都包含在此计算中,无论字段修饰符(静态、公共、私有等)如何。
例如,给定以下类和接口:
interface I0 {
int p = 0;
}
interface I1 extends I0 {
int x = 1;
}
interface I2 extends I0 {
int y = 2;
}
class C1 implements I1 {
public static int a = 3;
private int b = 4;
}
class C2 extends C1 implements I2 {
static int q = 5;
final int r = 6;
}
假设GetClassFields 调用C1 返回C1 的字段顺序为:a,b;并且 C2 的字段按顺序返回:q,r。类 C1 的实例将具有以下字段索引:
Field |
索引 |
Description |
a |
2 |
C1 实现的接口中的字段数为2(n=2): I0 的 p 和 I1 的 x 。 |
b |
3 |
后续索引。 |
类 C1 将具有相同的字段索引。
类 C2 的实例将具有以下字段索引:
Field |
索引 |
Description |
a |
3 |
C2 实现的接口中字段的个数是三个(n=3): I0 的p ,I1 的x 和I2 的y (C2 的接口)。请注意,I0 的字段 p 仅包含一次。 |
b |
4 |
随后的索引为“a”。 |
q |
5 |
“b”的后续索引。 |
r |
6 |
“q”的后续索引。 |
类 C2 将具有相同的字段索引。请注意,一个字段可能具有不同的索引,具体取决于正在查看它的对象——例如上面的字段“a”。另请注意:并非所有字段索引都可以从回调中看到,但所有索引都是出于说明目的而显示的。
接口 I1 将具有以下字段索引:
Field |
索引 |
Description |
x |
1 |
I1 的超接口中的字段数是一个(n=1): I0 的 p 。 |
|
数组引用的引用信息结构
为 JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT
引用返回的引用信息。
typedef struct {
jint index;
} jvmtiHeapReferenceInfoArray;
jvmtiHeapReferenceInfoArray
- 数组引用的引用信息结构
Field |
Type |
Description |
index |
jint |
数组索引。 |
常量池引用的引用信息结构
为 JVMTI_HEAP_REFERENCE_CONSTANT_POOL
引用返回的引用信息。
typedef struct {
jint index;
} jvmtiHeapReferenceInfoConstantPool;
jvmtiHeapReferenceInfoConstantPool
- 常量池引用的引用信息结构
Field |
Type |
Description |
index |
jint |
类常量池的索引。请参阅中的说明Java™ 虚拟机规范,第 4.4 章. |
局部变量引用的引用信息结构
为 JVMTI_HEAP_REFERENCE_STACK_LOCAL
引用返回的引用信息。
typedef struct {
jlong thread_tag;
jlong thread_id;
jint depth;
jmethodID method;
jlocation location;
jint slot;
} jvmtiHeapReferenceInfoStackLocal;
jvmtiHeapReferenceInfoStackLocal
- 局部变量引用的引用信息结构
Field |
Type |
Description |
thread_tag |
jlong |
与此堆栈对应的线程的标记,如果未标记则为零。 |
thread_id |
jlong |
这个栈对应的线程的唯一线程ID。 |
depth |
jint |
框架的深度。 |
method |
jmethodID |
在此框架中执行的方法。 |
location |
jlocation |
此框架中的当前执行位置。 |
slot |
jint |
局部变量的槽号。 |
JNI 本地引用的引用信息结构
为 JVMTI_HEAP_REFERENCE_JNI_LOCAL
引用返回的引用信息。
typedef struct {
jlong thread_tag;
jlong thread_id;
jint depth;
jmethodID method;
} jvmtiHeapReferenceInfoJniLocal;
jvmtiHeapReferenceInfoJniLocal
- JNI 本地引用的引用信息结构
Field |
Type |
Description |
thread_tag |
jlong |
与此堆栈对应的线程的标记,如果未标记则为零。 |
thread_id |
jlong |
这个栈对应的线程的唯一线程ID。 |
depth |
jint |
框架的深度。 |
method |
jmethodID |
在此框架中执行的方法。 |
其他参考的参考信息结构
为其他引用返回的引用信息。
typedef struct {
jlong reserved1;
jlong reserved2;
jlong reserved3;
jlong reserved4;
jlong reserved5;
jlong reserved6;
jlong reserved7;
jlong reserved8;
} jvmtiHeapReferenceInfoReserved;
jvmtiHeapReferenceInfoReserved
- 其他参考的参考信息结构
Field |
Type |
Description |
reserved1 |
jlong |
保留以供将来使用。 |
reserved2 |
jlong |
保留以供将来使用。 |
reserved3 |
jlong |
保留以供将来使用。 |
reserved4 |
jlong |
保留以供将来使用。 |
reserved5 |
jlong |
保留以供将来使用。 |
reserved6 |
jlong |
保留以供将来使用。 |
reserved7 |
jlong |
保留以供将来使用。 |
reserved8 |
jlong |
保留以供将来使用。 |
参考信息结构
返回的有关推荐人的信息。表示为各种参考信息的联合。
typedef union {
jvmtiHeapReferenceInfoField field;
jvmtiHeapReferenceInfoArray array;
jvmtiHeapReferenceInfoConstantPool constant_pool;
jvmtiHeapReferenceInfoStackLocal stack_local;
jvmtiHeapReferenceInfoJniLocal jni_local;
jvmtiHeapReferenceInfoReserved other;
} jvmtiHeapReferenceInfo;
堆回调函数结构
typedef struct {
jvmtiHeapIterationCallback heap_iteration_callback;
jvmtiHeapReferenceCallback heap_reference_callback;
jvmtiPrimitiveFieldCallback primitive_field_callback;
jvmtiArrayPrimitiveValueCallback array_primitive_value_callback;
jvmtiStringPrimitiveValueCallback string_primitive_value_callback;
jvmtiReservedCallback reserved5;
jvmtiReservedCallback reserved6;
jvmtiReservedCallback reserved7;
jvmtiReservedCallback reserved8;
jvmtiReservedCallback reserved9;
jvmtiReservedCallback reserved10;
jvmtiReservedCallback reserved11;
jvmtiReservedCallback reserved12;
jvmtiReservedCallback reserved13;
jvmtiReservedCallback reserved14;
jvmtiReservedCallback reserved15;
} jvmtiHeapCallbacks;
基本原理:堆转储功能(如下)使用每个对象的回调。虽然缓冲方法似乎可以提供更好的吞吐量,但测试并未表明情况如此——可能是由于内存引用的局部性或数组访问开销。
堆迭代回调
typedef jint (JNICALL *jvmtiHeapIterationCallback)
(jlong class_tag,
jlong size,
jlong* tag_ptr,
jint length,
void* user_data);
代理提供的回调函数。描述(但不传入)堆中的对象。
此函数应返回所需
访问控制标志 的位向量。这将确定是否应中止整个迭代(
JVMTI_VISIT_OBJECTS
标志被忽略)。
请参阅
堆回调函数限制。
参数
Name |
Type |
Description |
class_tag |
jlong |
对象类的标记(如果类未标记则为零)。如果对象表示运行时类,则 class_tag 是与 java.lang.Class 关联的标记(如果 java.lang.Class 未标记则为零)。 |
size |
jlong |
对象的大小(以字节为单位)。参见 GetObjectSize 。 |
tag_ptr |
jlong* |
对象标记值,如果对象未标记则为零。要设置与对象相关联的标签值,代理会设置参数指向的jlong 。 |
length |
jint |
如果此对象是数组,则为数组的长度。否则为负一 (-1)。 |
user_data |
void* |
用户提供的数据被传递到迭代函数中。 |
堆引用回调
typedef jint (JNICALL *jvmtiHeapReferenceCallback)
(jvmtiHeapReferenceKind reference_kind,
const jvmtiHeapReferenceInfo* reference_info,
jlong class_tag,
jlong referrer_class_tag,
jlong size,
jlong* tag_ptr,
jlong* referrer_tag_ptr,
jint length,
void* user_data);
代理提供的回调函数。描述从一个对象或 VM(引用者)到另一个对象(引用者)或堆根到引用者的引用。
此函数应返回所需
访问控制标志 的位向量。这将确定是否应该访问引用引用的对象,或者是否应该中止整个迭代。
请参阅
堆回调函数限制。
原始字段回调
typedef jint (JNICALL *jvmtiPrimitiveFieldCallback)
(jvmtiHeapReferenceKind kind,
const jvmtiHeapReferenceInfo* info,
jlong object_class_tag,
jlong* object_tag_ptr,
jvalue value,
jvmtiPrimitiveType value_type,
void* user_data);
代理提供的回调函数描述了对象的原始字段(
物体).原始字段是其类型为原始类型的字段。如果对象是一个类,这个回调将描述一个静态字段,否则将描述一个实例字段。
此函数应返回所需
访问控制标志 的位向量。这将确定是否应中止整个迭代(
JVMTI_VISIT_OBJECTS
标志被忽略)。
请参阅
堆回调函数限制。
数组原始值回调
typedef jint (JNICALL *jvmtiArrayPrimitiveValueCallback)
(jlong class_tag,
jlong size,
jlong* tag_ptr,
jint element_count,
jvmtiPrimitiveType element_type,
const void* elements,
void* user_data);
代理提供的回调函数。描述原始类型数组中的值。
此函数应返回所需
访问控制标志 的位向量。这将确定是否应中止整个迭代(
JVMTI_VISIT_OBJECTS
标志被忽略)。
请参阅
堆回调函数限制。
参数
Name |
Type |
Description |
class_tag |
jlong |
数组对象类的标记(如果类未标记则为零)。 |
size |
jlong |
数组的大小(以字节为单位)。参见 GetObjectSize 。 |
tag_ptr |
jlong* |
指向数组对象的标记,如果对象未标记则为零。要设置与对象相关联的标签值,代理会设置参数指向的jlong 。 |
element_count |
jint |
原始数组的长度。 |
element_type |
jvmtiPrimitiveType |
数组元素的类型。 |
elements |
const void* |
每个 element_count 项大小为 element_type 的打包数组中的数组元素。 |
user_data |
void* |
用户提供的数据被传递到迭代函数中。 |
字符串原始值回调
typedef jint (JNICALL *jvmtiStringPrimitiveValueCallback)
(jlong class_tag,
jlong size,
jlong* tag_ptr,
const jchar* value,
jint value_length,
void* user_data);
代理提供的回调函数。描述 java.lang.String 的值。
此函数应返回所需
访问控制标志 的位向量。这将确定是否应中止整个迭代(
JVMTI_VISIT_OBJECTS
标志被忽略)。
请参阅
堆回调函数限制。
参数
Name |
Type |
Description |
class_tag |
jlong |
String 类的类的标记(如果该类未标记则为零)。 |
size |
jlong |
字符串的大小(以字节为单位)。参见 GetObjectSize 。 |
tag_ptr |
jlong* |
指向 String 对象的标记,如果对象未标记则为零。要设置与对象相关联的标签值,代理会设置参数指向的jlong 。 |
value |
const jchar* |
String 的值,编码为 Unicode 字符串。 |
value_length |
jint |
字符串的长度。长度等于字符串中 16 位 Unicode 字符的数量。 |
user_data |
void* |
用户提供的数据被传递到迭代函数中。 |
保留供将来使用回调
typedef jint (JNICALL *jvmtiReservedCallback)
();
占位符——保留以供将来使用。
按照参考资料
jvmtiError
FollowReferences(jvmtiEnv* env,
jint heap_filter,
jclass klass,
jobject initial_object,
const jvmtiHeapCallbacks* callbacks,
const void* user_data)
此函数启动遍历可从指定对象直接和间接访问的对象,或者如果未指定 initial_object
,则遍历可从堆根访问的所有对象。堆根是一组系统类、JNI 全局变量、来自线程堆栈的引用,以及用作垃圾收集目的的根的其他对象。
该函数通过遍历参考图来运行。让A, B, ... 表示对象。当引用来自A到B被遍历,当引用从一个堆根到B被遍历,或者当B被指定为 initial_object
,然后B据说是参观过.来自的参考A到B直到A被访问。引用的报告顺序与遍历引用的顺序相同。通过调用代理提供的回调函数 jvmtiHeapReferenceCallback
来报告对象引用。在参考资料中A到B, A被称为referrer和B作为referree.对于来自引用者的每个引用,回调只被调用一次;即使存在引用循环或引用者的多条路径,也是如此。一个推荐人和一个推荐人之间可能有不止一个推荐人,每一个推荐人都会被报告。这些引用可以通过检查 jvmtiHeapReferenceCallback
回调的 reference_kind
和 reference_info
参数来区分。
此函数报告对象引用的 Java 编程语言视图,而不是虚拟机实现视图。以下对象引用在非空时会被报告:
- 实例对象报告对每个非原始实例字段(包括继承字段)的引用。
- 实例对象报告对对象类型(类)的引用。
- 类报告对超类和直接实现/扩展接口的引用。
- 类报告对类加载器、保护域、签名者和常量池中已解析条目的引用。
- 类报告对每个直接声明的非原始静态字段的引用。
- 数组报告对数组类型(类)和每个数组元素的引用。
- 原始数组报告对数组类型的引用。
此函数还可用于检查原始(非对象)值。访问对象后报告数组或字符串的原始值;通过调用代理提供的回调函数 jvmtiArrayPrimitiveValueCallback
或 jvmtiStringPrimitiveValueCallback
来报告。访问具有该字段的对象后,会报告一个原始字段;它通过调用代理提供的回调函数 jvmtiPrimitiveFieldCallback
来报告。
回调的提供与否NULL
只决定回调是否被调用,不影响访问哪些对象,也不影响是否调用其他回调。但是,jvmtiHeapReferenceCallback
返回的访问控制标志 确实确定当前对象引用的对象是否已访问。作为参数提供给此函数的 堆过滤器标志 和 klass
不控制访问哪些对象,但它们控制回调报告哪些对象和原始值。例如,如果设置的唯一回调是 array_primitive_value_callback
并且 klass
设置为字节数组类,则只会报告字节数组。下表总结了这一点:
在这个函数的执行过程中,堆的状态不会改变:没有对象被分配,没有对象被垃圾收集,对象的状态(包括持有的值)也没有改变。因此,执行 Java 编程语言代码的线程、尝试恢复执行 Java 编程语言代码的线程以及尝试执行 JNI 函数的线程通常会停止。
参数
Name |
Type |
Description |
heap_filter |
jint |
堆过滤器标志 的位向量。限制调用回调函数的对象。这适用于对象和原始回调。 |
klass |
jclass |
只有当对象是此类的实例时才会报告回调。不报告作为 klass 子类实例的对象。如果 klass 是接口,则不会报告任何对象。这适用于对象和原始回调。
如果 klass 是 NULL ,则回调不限于特定类的实例。 |
initial_object |
jobject |
跟随的对象
如果 initial_object 是 NULL ,则从堆根开始引用。 |
callbacks |
const jvmtiHeapCallbacks * |
定义回调函数集的结构。
Agent 传入一个指向 jvmtiHeapCallbacks 的指针。 |
user_data |
const void * |
用户提供的数据将传递给回调。
Agent 传入一个指针。如果 user_data 是 NULL ,则 NULL 作为用户提供的数据传递。 |
遍历堆
jvmtiError
IterateThroughHeap(jvmtiEnv* env,
jint heap_filter,
jclass klass,
const jvmtiHeapCallbacks* callbacks,
const void* user_data)
启动对堆中所有对象的迭代。这包括可到达和不可到达的对象。对象的访问没有特定顺序。
通过调用代理提供的回调函数 jvmtiHeapIterationCallback
来报告堆对象。不报告对象之间的引用。如果只需要可达对象,或者需要对象引用信息,请使用 FollowReferences
。
此函数还可用于检查原始(非对象)值。访问对象后报告数组或字符串的原始值;通过调用代理提供的回调函数 jvmtiArrayPrimitiveValueCallback
或 jvmtiStringPrimitiveValueCallback
来报告。访问具有该字段的对象后,会报告一个原始字段;它通过调用代理提供的回调函数 jvmtiPrimitiveFieldCallback
来报告。
除非迭代被回调返回的 堆访问控制标志 中止,否则将访问堆中的所有对象。回调的提供与否NULL
只决定回调是否被调用,不影响访问哪些对象,也不影响是否调用其他回调。作为参数提供给此函数的 堆过滤器标志 和 klass
不控制访问哪些对象,但它们控制回调报告哪些对象和原始值。例如,如果设置的唯一回调是 array_primitive_value_callback
并且 klass
设置为字节数组类,则只会报告字节数组。下表对此进行了总结(将其与 FollowReferences
进行对比):
在这个函数的执行过程中,堆的状态不会改变:没有对象被分配,没有对象被垃圾收集,对象的状态(包括持有的值)也没有改变。因此,执行 Java 编程语言代码的线程、尝试恢复执行 Java 编程语言代码的线程以及尝试执行 JNI 函数的线程通常会停止。
参数
Name |
Type |
Description |
heap_filter |
jint |
堆过滤器标志 的位向量。限制调用回调函数的对象。这适用于对象和原始回调。 |
klass |
jclass |
只有当对象是此类的实例时才会报告回调。不报告作为 klass 子类实例的对象。如果 klass 是接口,则不会报告任何对象。这适用于对象和原始回调。
如果 klass 是 NULL ,则回调不限于特定类的实例。 |
callbacks |
const jvmtiHeapCallbacks * |
定义集合回调函数的结构。
Agent 传入一个指向 jvmtiHeapCallbacks 的指针。 |
user_data |
const void * |
用户提供的数据将传递给回调。
Agent 传入一个指针。如果 user_data 是 NULL ,则 NULL 作为用户提供的数据传递。 |
获取标签
jvmtiError
GetTag(jvmtiEnv* env,
jobject object,
jlong* tag_ptr)
检索与对象关联的标签。标记是一个长值,通常用于存储唯一标识符或指向对象信息的指针。标签设置为 SetTag
。没有设置标签的对象返回零标签值。
参数
Name |
Type |
Description |
object |
jobject |
要检索其标记的对象。 |
tag_ptr |
jlong* |
返回时,引用的 long 被设置为标签的值。
Agent 传递一个指向 jlong 的指针。返回时,jlong 已设置。 |
设置标签
jvmtiError
SetTag(jvmtiEnv* env,
jobject object,
jlong tag)
设置与对象关联的标签。标记是一个长值,通常用于存储唯一标识符或指向对象信息的指针。使用 GetTag
可以看到标签。
参数
Name |
Type |
Description |
object |
jobject |
要设置其标签的对象。 |
tag |
jlong |
标签的新值。 |
获取带有标签的对象
jvmtiError
GetObjectsWithTags(jvmtiEnv* env,
jint tag_count,
const jlong* tags,
jint* count_ptr,
jobject** object_result_ptr,
jlong** tag_result_ptr)
返回堆中具有指定标签的对象。格式是对象和标签的并行数组。
参数
Name |
Type |
Description |
tag_count |
jint |
要扫描的标签数。 |
tags |
const jlong * |
扫描带有这些标签的对象。此数组中不允许使用零。
代理传入 jlong 的 tag_count 元素数组。 |
count_ptr |
jint * |
返回具有 tags 中任何标签的对象数。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
object_result_ptr |
jobject ** |
返回具有 tags 中任何标记的对象数组。
Agent 传递一个指向 jobject* 的指针。返回时,jobject* 指向新分配的大小为 *count_ptr 的数组。该数组应使用 Deallocate 释放。如果 object_result_ptr 是 NULL ,则不会返回此信息。 object_result_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
tag_result_ptr |
jlong ** |
对于 object_result_ptr 中的每个对象,返回相应索引处的标签。
Agent 传递一个指向 jlong* 的指针。返回时,jlong* 指向新分配的大小为 *count_ptr 的数组。该数组应使用 Deallocate 释放。如果 tag_result_ptr 是 NULL ,则不会返回此信息。 |
强制垃圾收集
jvmtiError
ForceGarbageCollection(jvmtiEnv* env)
强制 VM 执行垃圾回收。垃圾收集尽可能完整。此函数不会导致终结器运行。垃圾收集完成后,此函数才会返回。
尽管垃圾收集尽可能完整,但不能保证在该函数返回时所有 ObjectFree
事件都已发送。特别是,可能会阻止对象被释放,因为它正在等待完成。
堆 (1.0)
堆 (1.0) 函数:
堆 (1.0) 函数类型:
堆 (1.0) 类型:
这些函数和数据类型是在最初的JVM中引入的 TI 版本 1.0。它们已被弃用,并将更改为在未来的版本中返回错误。它们在 JVM 中被取代 TI 1.2 版 (Java SE 6) 更多 强大而灵活的版本 哪个:
- 允许访问原始值(字符串、数组和原始字段的值)
- 允许设置 referrer 的标签,从而实现更高效的本地化参考图构建
- 提供更广泛的过滤能力
- 是可扩展的,允许它们的能力在未来的 JVM 版本中增长 TI
请使用 当前堆函数 .
堆对象过滤器枚举 (jvmtiHeapObjectFilter
)
持续的 |
Value |
Description |
JVMTI_HEAP_OBJECT_TAGGED |
1 |
仅标记对象。 |
JVMTI_HEAP_OBJECT_UNTAGGED |
2 |
仅限未标记的对象。 |
JVMTI_HEAP_OBJECT_EITHER |
3 |
标记或未标记的对象。 |
堆根类型枚举 (jvmtiHeapRootKind
)
持续的 |
Value |
Description |
JVMTI_HEAP_ROOT_JNI_GLOBAL |
1 |
JNI 全局引用。 |
JVMTI_HEAP_ROOT_SYSTEM_CLASS |
2 |
系统类。 |
JVMTI_HEAP_ROOT_MONITOR |
3 |
监视器。 |
JVMTI_HEAP_ROOT_STACK_LOCAL |
4 |
堆栈本地。 |
JVMTI_HEAP_ROOT_JNI_LOCAL |
5 |
JNI 本地引用。 |
JVMTI_HEAP_ROOT_THREAD |
6 |
线。 |
JVMTI_HEAP_ROOT_OTHER |
7 |
其他。 |
对象引用枚举 (jvmtiObjectReferenceKind
)
持续的 |
Value |
Description |
JVMTI_REFERENCE_CLASS |
1 |
从一个对象到它的类的引用。 |
JVMTI_REFERENCE_FIELD |
2 |
从对象到其实例字段之一的值的引用。对于此类引用,jvmtiObjectReferenceCallback 的referrer_index 参数是实例字段的索引。索引基于所有对象字段的顺序。这包括类中直接声明的静态和实例字段的所有字段,包括在超类和超接口中声明的所有字段(公共和私有)字段。因此,索引的计算方法是将直接声明的类中字段的索引(参见 GetClassFields )与所有超类和超接口中声明的字段总数(公共和私有)相加。索引从零开始。 |
JVMTI_REFERENCE_ARRAY_ELEMENT |
3 |
从数组到其元素之一的引用。对于此类引用,jvmtiObjectReferenceCallback 的referrer_index 参数是数组索引。 |
JVMTI_REFERENCE_CLASS_LOADER |
4 |
从一个类到它的类加载器的引用。 |
JVMTI_REFERENCE_SIGNERS |
5 |
从一个类到它的 signers 数组的引用。 |
JVMTI_REFERENCE_PROTECTION_DOMAIN |
6 |
从一个类到它的保护域的引用。 |
JVMTI_REFERENCE_INTERFACE |
7 |
从一个类到它的接口之一的引用。 |
JVMTI_REFERENCE_STATIC_FIELD |
8 |
从一个类到它的静态字段之一的值的引用。对于此类引用,jvmtiObjectReferenceCallback 的referrer_index 参数是静态字段的索引。索引基于所有对象字段的顺序。这包括类中直接声明的静态和实例字段的所有字段,包括在超类和超接口中声明的所有字段(公共和私有)字段。因此,索引的计算方法是将直接声明的类中字段的索引(参见 GetClassFields )与所有超类和超接口中声明的字段总数(公共和私有)相加。索引从零开始。注意:此定义与 JVM 中的不同 TI 1.0 规范。理由:没有已知的实现使用 1.0 定义。 |
JVMTI_REFERENCE_CONSTANT_POOL |
9 |
从类到常量池中已解析条目的引用。对于此类引用,jvmtiObjectReferenceCallback 的 referrer_index 参数是该类常量池表的索引,从 1 开始。参见Java™ 虚拟机规范,第 4.4 章. |
迭代控制枚举(jvmtiIterationControl
)
持续的 |
Value |
Description |
JVMTI_ITERATION_CONTINUE |
1 |
继续迭代。如果这是引用迭代,请遵循此对象的引用。 |
JVMTI_ITERATION_IGNORE |
2 |
继续迭代。如果这是一个引用迭代,忽略这个对象的引用。 |
JVMTI_ITERATION_ABORT |
0 |
中止迭代。 |
堆对象回调
typedef jvmtiIterationControl (JNICALL *jvmtiHeapObjectCallback)
(jlong class_tag,
jlong size,
jlong* tag_ptr,
void* user_data);
代理提供的回调函数。描述(但不传入)堆中的对象。
返回值应为
JVMTI_ITERATION_CONTINUE
以继续迭代,或
JVMTI_ITERATION_ABORT
以停止迭代。
请参阅
堆回调函数限制。
参数
Name |
Type |
Description |
class_tag |
jlong |
对象类的标记(如果类未标记则为零)。如果对象表示运行时类,则 class_tag 是与 java.lang.Class 关联的标记(如果 java.lang.Class 未标记则为零)。 |
size |
jlong |
对象的大小(以字节为单位)。参见 GetObjectSize 。 |
tag_ptr |
jlong* |
对象标记值,如果对象未标记则为零。要设置与对象相关联的标签值,代理会设置参数指向的jlong 。 |
user_data |
void* |
用户提供的数据被传递到迭代函数中。 |
堆根对象回调
typedef jvmtiIterationControl (JNICALL *jvmtiHeapRootCallback)
(jvmtiHeapRootKind root_kind,
jlong class_tag,
jlong size,
jlong* tag_ptr,
void* user_data);
代理提供的回调函数。描述(但不传入)作为垃圾收集目的根的对象。
返回值应该是
JVMTI_ITERATION_CONTINUE
继续迭代,
JVMTI_ITERATION_IGNORE
继续迭代而不从 referree 对象寻求引用或
JVMTI_ITERATION_ABORT
停止迭代。
请参阅
堆回调函数限制。
参数
Name |
Type |
Description |
root_kind |
jvmtiHeapRootKind |
堆根的那种。 |
class_tag |
jlong |
对象类的标记(如果类未标记则为零)。如果对象表示运行时类,则 class_tag 是与 java.lang.Class 关联的标记(如果 java.lang.Class 未标记则为零)。 |
size |
jlong |
对象的大小(以字节为单位)。参见 GetObjectSize 。 |
tag_ptr |
jlong* |
对象标记值,如果对象未标记则为零。要设置与对象相关联的标签值,代理会设置参数指向的jlong 。 |
user_data |
void* |
用户提供的数据被传递到迭代函数中。 |
堆栈引用对象回调
typedef jvmtiIterationControl (JNICALL *jvmtiStackReferenceCallback)
(jvmtiHeapRootKind root_kind,
jlong class_tag,
jlong size,
jlong* tag_ptr,
jlong thread_tag,
jint depth,
jmethodID method,
jint slot,
void* user_data);
代理提供的回调函数。描述(但不传入)堆栈上的对象,该对象是用于垃圾收集的根。
返回值应该是
JVMTI_ITERATION_CONTINUE
继续迭代,
JVMTI_ITERATION_IGNORE
继续迭代而不从 referree 对象寻求引用或
JVMTI_ITERATION_ABORT
停止迭代。
请参阅
堆回调函数限制。
参数
Name |
Type |
Description |
root_kind |
jvmtiHeapRootKind |
根的种类(JVMTI_HEAP_ROOT_STACK_LOCAL 或 JVMTI_HEAP_ROOT_JNI_LOCAL )。 |
class_tag |
jlong |
对象类的标记(如果类未标记则为零)。如果对象表示运行时类,则 class_tag 是与 java.lang.Class 关联的标记(如果 java.lang.Class 未标记则为零)。 |
size |
jlong |
对象的大小(以字节为单位)。参见 GetObjectSize 。 |
tag_ptr |
jlong* |
对象标记值,如果对象未标记则为零。要设置与对象相关联的标签值,代理会设置参数指向的jlong 。 |
thread_tag |
jlong |
与此堆栈对应的线程的标记,如果未标记则为零。 |
depth |
jint |
框架的深度。 |
method |
jmethodID |
在此框架中执行的方法。 |
slot |
jint |
插槽号。 |
user_data |
void* |
用户提供的数据被传递到迭代函数中。 |
对象引用回调
typedef jvmtiIterationControl (JNICALL *jvmtiObjectReferenceCallback)
(jvmtiObjectReferenceKind reference_kind,
jlong class_tag,
jlong size,
jlong* tag_ptr,
jlong referrer_tag,
jint referrer_index,
void* user_data);
代理提供的回调函数。描述从一个对象(引用者)到另一个对象(引用者)的引用。
返回值应该是
JVMTI_ITERATION_CONTINUE
继续迭代,
JVMTI_ITERATION_IGNORE
继续迭代而不从 referree 对象寻求引用或
JVMTI_ITERATION_ABORT
停止迭代。
请参阅
堆回调函数限制。
遍历从对象可达的对象
jvmtiError
IterateOverObjectsReachableFromObject(jvmtiEnv* env,
jobject object,
jvmtiObjectReferenceCallback object_reference_callback,
const void* user_data)
此函数遍历所有可从指定对象直接或间接到达的对象。对于每个对象A(称为引用者)引用对象B调用指定的回调函数来描述对象引用。对于来自引用者的每个引用,回调只被调用一次;即使存在引用循环或引用者的多条路径,也是如此。 referrer 和 referree 之间可能有不止一个引用,这些可以通过 jvmtiObjectReferenceCallback.reference_kind
和 jvmtiObjectReferenceCallback.referrer_index
来区分。对象的回调将始终在其引用者的回调之后发生。
有关报告的对象引用,请参阅 FollowReferences
。
在这个函数的执行过程中,堆的状态不会改变:没有对象被分配,没有对象被垃圾收集,对象的状态(包括持有的值)也没有改变。因此,执行 Java 编程语言代码的线程、尝试恢复执行 Java 编程语言代码的线程以及尝试执行 JNI 函数的线程通常会停止。
参数
Name |
Type |
Description |
object |
jobject |
物体 |
object_reference_callback |
jvmtiObjectReferenceCallback |
要调用的回调来描述每个对象引用。
|
user_data |
const void * |
用户提供的数据将传递给回调。
Agent 传入一个指针。如果 user_data 是 NULL ,则 NULL 作为用户提供的数据传递。 |
遍历可达对象
jvmtiError
IterateOverReachableObjects(jvmtiEnv* env,
jvmtiHeapRootCallback heap_root_callback,
jvmtiStackReferenceCallback stack_ref_callback,
jvmtiObjectReferenceCallback object_ref_callback,
const void* user_data)
此函数遍历根对象以及从根对象直接和间接可达的所有对象。根对象包括一组系统类、JNI 全局变量、来自线程堆栈的引用,以及用作垃圾收集目的的根的其他对象。
对于每个根,调用 heap_root_callback
或 stack_ref_callback
回调。一个对象可以出于多种原因成为根对象,在这种情况下,针对每种原因调用适当的回调。
对于每个对象引用,调用 object_ref_callback
回调函数来描述对象引用。对于来自引用者的每个引用,回调只被调用一次;即使存在引用循环或引用者的多条路径,也是如此。 referrer 和 referree 之间可能有不止一个引用,这些可以通过 jvmtiObjectReferenceCallback.reference_kind
和 jvmtiObjectReferenceCallback.referrer_index
来区分。对象的回调将始终在其引用者的回调之后发生。
有关报告的对象引用,请参阅 FollowReferences
。
根总是在报告任何对象引用之前报告给探查器。换句话说,在为所有根调用适当的回调之前,不会调用 object_ref_callback
回调。如果 object_ref_callback
回调指定为 NULL
,则此函数会在向探查器报告根对象后返回。
在这个函数的执行过程中,堆的状态不会改变:没有对象被分配,没有对象被垃圾收集,对象的状态(包括持有的值)也没有改变。因此,执行 Java 编程语言代码的线程、尝试恢复执行 Java 编程语言代码的线程以及尝试执行 JNI 函数的线程通常会停止。
参数
Name |
Type |
Description |
heap_root_callback |
jvmtiHeapRootCallback |
为 JVMTI_HEAP_ROOT_JNI_GLOBAL 、 JVMTI_HEAP_ROOT_SYSTEM_CLASS 、 JVMTI_HEAP_ROOT_MONITOR 、 JVMTI_HEAP_ROOT_THREAD 或 JVMTI_HEAP_ROOT_OTHER 类型的每个堆根调用的回调函数。
如果 heap_root_callback 是 NULL ,则不报告堆根。 |
stack_ref_callback |
jvmtiStackReferenceCallback |
为 JVMTI_HEAP_ROOT_STACK_LOCAL 或 JVMTI_HEAP_ROOT_JNI_LOCAL 的每个堆根调用的回调函数。
如果 stack_ref_callback 是 NULL ,则不报告堆栈引用。 |
object_ref_callback |
jvmtiObjectReferenceCallback |
为每个对象引用调用的回调函数。
如果 object_ref_callback 是 NULL ,则不要遵循根对象的引用。 |
user_data |
const void * |
用户提供的数据将传递给回调。
Agent 传入一个指针。如果 user_data 是 NULL ,则 NULL 作为用户提供的数据传递。 |
遍历堆
jvmtiError
IterateOverHeap(jvmtiEnv* env,
jvmtiHeapObjectFilter object_filter,
jvmtiHeapObjectCallback heap_object_callback,
const void* user_data)
遍历堆中的所有对象。这包括可到达和不可到达的对象。
object_filter
参数表示调用回调函数的对象。如果此参数为 JVMTI_HEAP_OBJECT_TAGGED
,则只会为每个被标记的对象调用回调。如果参数是 JVMTI_HEAP_OBJECT_UNTAGGED
那么回调将只针对未标记的对象。如果参数是 JVMTI_HEAP_OBJECT_EITHER
那么回调函数将被调用堆中的每个对象,不管它是否被标记。
在这个函数的执行过程中,堆的状态不会改变:没有对象被分配,没有对象被垃圾收集,对象的状态(包括持有的值)也没有改变。因此,执行 Java 编程语言代码的线程、尝试恢复执行 Java 编程语言代码的线程以及尝试执行 JNI 函数的线程通常会停止。
迭代类的实例
jvmtiError
IterateOverInstancesOfClass(jvmtiEnv* env,
jclass klass,
jvmtiHeapObjectFilter object_filter,
jvmtiHeapObjectCallback heap_object_callback,
const void* user_data)
遍历堆中作为指定类实例的所有对象。这包括指定类的直接实例和指定类的所有子类的实例。这包括可到达和不可到达的对象。
object_filter
参数表示调用回调函数的对象。如果此参数为 JVMTI_HEAP_OBJECT_TAGGED
,则只会为每个被标记的对象调用回调。如果参数是 JVMTI_HEAP_OBJECT_UNTAGGED
则只会为未标记的对象调用回调。如果参数是 JVMTI_HEAP_OBJECT_EITHER
那么回调函数将被调用堆中的每个对象,不管它是否被标记。
在这个函数的执行过程中,堆的状态不会改变:没有对象被分配,没有对象被垃圾收集,对象的状态(包括持有的值)也没有改变。因此,执行 Java 编程语言代码的线程、尝试恢复执行 Java 编程语言代码的线程以及尝试执行 JNI 函数的线程通常会停止。
局部变量
局部变量函数:
这些函数用于检索或设置局部变量的值。变量由包含其值的帧的深度和该帧中变量的槽号来标识。可以使用函数 GetLocalVariableTable
获得变量到槽号的映射。
GetLocalXXX
函数可用于检索包含在虚拟线程帧中的局部变量的值。 SetLocalXXX
函数可用于在断点或单步事件处挂起的虚拟线程的最顶层帧中设置局部变量的值。在其他情况下,实现可能支持设置局部变量。
获取局部变量 - 对象
jvmtiError
GetLocalObject(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jobject* value_ptr)
此函数可用于检索类型为 Object
或 Object
的子类的局部变量的值。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
包含变量值的框架线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
包含变量值的帧的深度。 |
slot |
jint |
变量的槽号。 |
value_ptr |
jobject* |
返回时,指向变量的值。
代理传递一个指向 jobject 的指针。返回时,jobject 已设置。 value_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
获取本地实例
jvmtiError
GetLocalInstance(jvmtiEnv* env,
jthread thread,
jint depth,
jobject* value_ptr)
此函数可用于从非静态帧中检索槽 0 处的局部对象变量(“this
”对象)的值。此函数可以从本机方法帧中检索“this
”对象,而 GetLocalObject()
在这些情况下会返回 JVMTI_ERROR_OPAQUE_FRAME
。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
包含变量值的框架线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
包含变量值的帧的深度。 |
value_ptr |
jobject* |
返回时,指向变量的值。
代理传递一个指向 jobject 的指针。返回时,jobject 已设置。 value_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
获取局部变量 - Int
jvmtiError
GetLocalInt(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jint* value_ptr)
此函数可用于检索类型为 int
、short
、char
、byte
或 boolean
的局部变量的值。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
包含变量值的框架线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
包含变量值的帧的深度。 |
slot |
jint |
变量的槽号。 |
value_ptr |
jint* |
返回时,指向变量的值。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
获取局部变量 - 长
jvmtiError
GetLocalLong(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jlong* value_ptr)
此函数可用于检索类型为 long
的局部变量的值。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
包含变量值的框架线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
包含变量值的帧的深度。 |
slot |
jint |
变量的槽号。 |
value_ptr |
jlong* |
返回时,指向变量的值。
Agent 传递一个指向 jlong 的指针。返回时,jlong 已设置。 |
获取局部变量 - Float
jvmtiError
GetLocalFloat(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jfloat* value_ptr)
此函数可用于检索类型为 float
的局部变量的值。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
包含变量值的框架线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
包含变量值的帧的深度。 |
slot |
jint |
变量的槽号。 |
value_ptr |
jfloat* |
返回时,指向变量的值。
Agent 传递一个指向 jfloat 的指针。返回时,jfloat 已设置。 |
获取局部变量 - 双精度
jvmtiError
GetLocalDouble(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jdouble* value_ptr)
此函数可用于检索类型为 long
的局部变量的值。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
包含变量值的框架线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
包含变量值的帧的深度。 |
slot |
jint |
变量的槽号。 |
value_ptr |
jdouble* |
返回时,指向变量的值。
Agent 传递一个指向 jdouble 的指针。返回时,jdouble 已设置。 |
设置局部变量 - 对象
jvmtiError
SetLocalObject(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jobject value)
此函数可用于设置类型为 Object
或 Object
的子类的局部变量的值。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
包含变量值的框架线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
包含变量值的帧的深度。 |
slot |
jint |
变量的槽号。 |
value |
jobject |
变量的新值。 |
设置局部变量 - Int
jvmtiError
SetLocalInt(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jint value)
此函数可用于设置类型为 int
、short
、char
、byte
或 boolean
的局部变量的值。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
包含变量值的框架线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
包含变量值的帧的深度。 |
slot |
jint |
变量的槽号。 |
value |
jint |
变量的新值。 |
设置局部变量 - 长
jvmtiError
SetLocalLong(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jlong value)
此函数可用于设置类型为 long
的局部变量的值。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
包含变量值的框架线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
包含变量值的帧的深度。 |
slot |
jint |
变量的槽号。 |
value |
jlong |
变量的新值。 |
设置局部变量 - Float
jvmtiError
SetLocalFloat(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jfloat value)
此函数可用于设置类型为 float
的局部变量的值。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
包含变量值的框架线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
包含变量值的帧的深度。 |
slot |
jint |
变量的槽号。 |
value |
jfloat |
变量的新值。 |
设置局部变量 - 双精度
jvmtiError
SetLocalDouble(jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jdouble value)
此函数可用于设置类型为 double
的局部变量的值。
指定的线程必须挂起或者必须是当前线程。
参数
Name |
Type |
Description |
thread |
jthread |
包含变量值的框架线程。如果 thread 是 NULL ,则使用当前线程。 |
depth |
jint |
包含变量值的帧的深度。 |
slot |
jint |
变量的槽号。 |
value |
jdouble |
变量的新值。 |
断点
断点函数:
设置断点
jvmtiError
SetBreakpoint(jvmtiEnv* env,
jmethodID method,
jlocation location)
在 method
和 location
指示的指令处设置断点。一条指令只能有一个断点。
每当指定的指令即将被执行时,就会产生一个Breakpoint
事件。
清除断点
jvmtiError
ClearBreakpoint(jvmtiEnv* env,
jmethodID method,
jlocation location)
清除 method
和 location
指示的字节码处的断点。
看场
监视字段函数:
设置现场访问监视
jvmtiError
SetFieldAccessWatch(jvmtiEnv* env,
jclass klass,
jfieldID field)
当klass
和field
指定的字段即将被访问时产生一个FieldAccess
事件。每次访问该字段都会生成一个事件,直到它被 ClearFieldAccessWatch
取消。监视来自 Java 编程语言代码或 JNI 代码的字段访问,不监视通过其他方式修改的字段。请注意,JVM TI 用户应注意,他们自己的现场访问会触发手表。一个字段只能有一个字段访问监视集。字段的修改不被视为访问——使用 SetFieldModificationWatch
来监视修改。
参数
Name |
Type |
Description |
klass |
jclass |
包含要观看的字段的类 |
field |
jfieldID |
值得关注的字段 |
清晰的现场访问手表
jvmtiError
ClearFieldAccessWatch(jvmtiEnv* env,
jclass klass,
jfieldID field)
在 klass
和 field
指定的字段上取消先前由 SetFieldAccessWatch
设置的字段访问监视。
参数
Name |
Type |
Description |
klass |
jclass |
包含要观看的字段的类 |
field |
jfieldID |
值得关注的字段 |
设置字段修改监视
jvmtiError
SetFieldModificationWatch(jvmtiEnv* env,
jclass klass,
jfieldID field)
当由klass
和field
指定的字段即将被修改时产生一个FieldModification
事件。每次修改字段都会生成一个事件,直到用 ClearFieldModificationWatch
取消。监视来自 Java 编程语言代码或 JNI 代码的字段修改,不监视通过其他方式修改的字段。请注意,JVM TI 用户应注意自己的现场修改会触发手表。一个字段只能有一个字段修改监视集。
参数
Name |
Type |
Description |
klass |
jclass |
包含要观看的字段的类 |
field |
jfieldID |
值得关注的字段 |
清除字段修改手表
jvmtiError
ClearFieldModificationWatch(jvmtiEnv* env,
jclass klass,
jfieldID field)
在 klass
和 field
指定的字段上取消先前由 SetFieldModificationWatch
设置的字段修改监视。
参数
Name |
Type |
Description |
klass |
jclass |
包含要观看的字段的类 |
field |
jfieldID |
值得关注的字段 |
Module
模块功能:
获取所有模块
jvmtiError
GetAllModules(jvmtiEnv* env,
jint* module_count_ptr,
jobject** modules_ptr)
返回虚拟机中加载的所有模块的数组。该数组包括每个类加载器的未命名模块。数组中的模块数通过 module_count_ptr
返回,数组本身通过 modules_ptr
返回。
参数
Name |
Type |
Description |
module_count_ptr |
jint* |
返回时,指向返回模块的数量。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
modules_ptr |
jobject** |
返回时,指向一组引用,每个模块一个。
Agent 传递一个指向 jobject* 的指针。返回时,jobject* 指向新分配的大小为 *module_count_ptr 的数组。该数组应使用 Deallocate 释放。 modules_ptr 返回的对象是 JNI 本地引用并且必须是 管理 。 |
获取命名模块
jvmtiError
GetNamedModule(jvmtiEnv* env,
jobject class_loader,
const char* package_name,
jobject* module_ptr)
返回定义给包含给定包的类加载器的命名模块的 java.lang.Module
对象。该模块通过 module_ptr
返回。
如果为类加载器定义了命名模块并且它包含包,则返回该命名模块,否则返回NULL
。
参数
Name |
Type |
Description |
class_loader |
jobject |
一个类加载器。如果 class_loader 不是 NULL 或 java.lang.ClassLoader 的子类,则此函数返回 JVMTI_ERROR_ILLEGAL_ARGUMENT 。
如果 class_loader 是 NULL ,则假定引导加载程序。 |
package_name |
const char* |
包的名称,编码为 修改后的 UTF-8 字符串。包名采用内部形式(JVMS 4.2.1);标识符由正斜杠而不是句点分隔。
代理传入一个 char 数组。 |
module_ptr |
jobject* |
返回时,指向 java.lang.Module 对象或指向 NULL 。
代理传递一个指向 jobject 的指针。返回时,jobject 已设置。 module_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
添加模块读取
jvmtiError
AddModuleReads(jvmtiEnv* env,
jobject module,
jobject to_module)
更新模块以读取另一个模块。当 module
是未命名模块时,此函数是空操作。此函数有助于在命名模块中检测代码,其中该检测需要扩展模块读取的模块集。
参数
Name |
Type |
Description |
module |
jobject |
要更新的模块。
|
to_module |
jobject |
要读取的附加模块。
|
添加模块导出
jvmtiError
AddModuleExports(jvmtiEnv* env,
jobject module,
const char* pkg_name,
jobject to_module)
更新模块以将包导出到另一个模块。当 module
是未命名模块或打开模块时,此函数是空操作。此函数有助于在命名模块中检测代码,其中该检测需要扩展模块导出的包集。
添加模块打开
jvmtiError
AddModuleOpens(jvmtiEnv* env,
jobject module,
const char* pkg_name,
jobject to_module)
更新模块以将包打开到另一个模块。当 module
是未命名模块或打开模块时,此函数是空操作。此功能有助于在模块中检测代码,其中该检测需要扩展模块向其他模块打开的包集。
添加模块使用
jvmtiError
AddModuleUses(jvmtiEnv* env,
jobject module,
jclass service)
更新模块以将服务添加到模块使用的服务集中。当模块是未命名模块时,此函数是空操作。此功能有助于在命名模块中检测代码,其中该检测需要扩展模块正在使用的服务集。
参数
Name |
Type |
Description |
module |
jobject |
要更新的模块。
|
service |
jclass |
要使用的服务。
|
添加模块提供
jvmtiError
AddModuleProvides(jvmtiEnv* env,
jobject module,
jclass service,
jclass impl_class)
更新模块以将服务添加到模块提供的服务集中。当模块是未命名模块时,此函数是空操作。此功能有助于在命名模块中检测代码,其中该检测需要更改所提供的服务。
参数
Name |
Type |
Description |
module |
jobject |
要更新的模块。
|
service |
jclass |
要提供的服务。
|
impl_class |
jclass |
所提供服务的实现类。
|
是可修改模块
jvmtiError
IsModifiableModule(jvmtiEnv* env,
jobject module,
jboolean* is_modifiable_module_ptr)
确定模块是否可修改。如果模块是可修改的,则可以使用 AddModuleReads
、AddModuleExports
、AddModuleOpens
、AddModuleUses
和 AddModuleProvides
更新该模块。如果模块不可修改,则无法使用这些函数更新模块。当调用此函数以确定未命名模块是否可修改时,此函数的结果始终为 JNI_TRUE
。
参数
Name |
Type |
Description |
module |
jobject |
要查询的模块。
|
is_modifiable_module_ptr |
jboolean* |
返回时,指向此函数的布尔结果。
Agent 传递一个指向 jboolean 的指针。返回时,jboolean 已设置。 |
Class
类函数:
类类型:
类标志和常量:
获取加载类
jvmtiError
GetLoadedClasses(jvmtiEnv* env,
jint* class_count_ptr,
jclass** classes_ptr)
返回虚拟机中加载的所有类的数组。数组中类的数量通过 class_count_ptr
返回,数组本身通过 classes_ptr
返回。
类或接口的创建可以由以下之一触发:
- 通过使用类加载器从
class
文件表示加载和派生类(参见Java™ 虚拟机规范,第 5.3 章).
- 通过调用 Lookup::defineHiddenClass 从
class
文件表示创建隐藏类或接口。
- 通过调用某些 Java SE 平台 API 中的方法,例如反射。
数组类由 Java 虚拟机直接创建。创建可以通过使用类加载器或通过调用某些 Java SE 平台 API 中的方法(例如反射)来触发。
返回的列表包括所有类和接口,包括 隐藏类或接口 ,以及所有类型的数组类(包括原始类型数组)。原始类(例如 java.lang.Integer.TYPE
)是不是包含在返回列表中。
参数
Name |
Type |
Description |
class_count_ptr |
jint* |
返回时,指向类的数量。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
classes_ptr |
jclass** |
返回时,指向一组引用,每个类一个。
Agent 传递一个指向 jclass* 的指针。返回时,jclass* 指向新分配的大小为 *class_count_ptr 的数组。该数组应使用 Deallocate 释放。 classes_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
获取类加载器类
jvmtiError
GetClassLoaderClasses(jvmtiEnv* env,
jobject initiating_loader,
jint* class_count_ptr,
jclass** classes_ptr)
返回此类加载器可以通过名称通过 ClassLoader::loadClass 、 Class::forName 和字节码链接找到的所有类的数组。也就是说,initiating_loader
已被记录为初始加载程序的所有类。返回数组中的每个类都是由此类加载器创建的,可以直接定义它,也可以委托给另一个类加载器。看Java™ 虚拟机规范,第 5.3 章.
返回的列表不包括元素类型为隐藏类或接口的 隐藏类或接口 或数组类,因为它们无法被任何类加载器发现。
数组中类的数量通过 class_count_ptr
返回,数组本身通过 classes_ptr
返回。
参见 Lookup::defineHiddenClass
参数
Name |
Type |
Description |
initiating_loader |
jobject |
启动类加载器。
如果 initiating_loader 是 NULL ,将返回引导加载程序启动的类。 |
class_count_ptr |
jint* |
返回时,指向类的数量。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
classes_ptr |
jclass** |
返回时,指向一组引用,每个类一个。
Agent 传递一个指向 jclass* 的指针。返回时,jclass* 指向新分配的大小为 *class_count_ptr 的数组。该数组应使用 Deallocate 释放。 classes_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
获取类签名
jvmtiError
GetClassSignature(jvmtiEnv* env,
jclass klass,
char** signature_ptr,
char** generic_ptr)
返回 klass
指示的类的名称和通用签名。
如果该类是类或接口,则:
- 如果类或接口不是 hidden ,则返回的名称是 JNI 类型签名 。例如,java.util.List 是“Ljava/util/List;”
- 如果类或接口是 hidden ,则返回的名称是以下形式的字符串:
"L" + N + "." + S + ";"
其中 N
是由传递给 Lookup::defineHiddenClass 的 class
文件指示的以内部形式 (JVMS 4.2.1) 编码的二进制名称,而 S
是不合格的姓名。返回的名称不是类型描述符,不符合 JVMS 4.3.2。例如,com.foo.Foo/AnySuffix 是“Lcom/foo/Foo.AnySuffix;”
如果 klass
指示的类表示数组类,则返回的名称是由一个或多个“[
”字符组成的字符串,表示数组嵌套的深度,后跟元素类型的类签名。例如 java.lang.String[] 的类签名是“[Ljava/lang/String;”而 int[] 的是“[I”。
如果 klass
指示的类表示原始类型或 void
,则返回的名称是 相应原始类型的类型签名字符 。例如,java.lang.Integer.TYPE 是“I”。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
signature_ptr |
char ** |
返回时,指向类的 JNI 类型签名,编码为 修改后的 UTF-8 字符串。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。如果 signature_ptr 是 NULL ,则不返回签名。 |
generic_ptr |
char ** |
返回时,指向类的通用签名,编码为 修改后的 UTF-8 字符串。如果该类没有通用签名属性,则返回时指向 NULL 。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。如果 generic_ptr 是 NULL ,则不返回通用签名。 |
获取类状态
jvmtiError
GetClassStatus(jvmtiEnv* env,
jclass klass,
jint* status_ptr)
获取类的状态。可以设置零个或多个以下位。
类状态标志
持续的 |
Value |
Description |
JVMTI_CLASS_STATUS_VERIFIED |
1 |
类字节码已经过验证 |
JVMTI_CLASS_STATUS_PREPARED |
2 |
备课完成 |
JVMTI_CLASS_STATUS_INITIALIZED |
4 |
类初始化完成。静态初始化程序已运行。 |
JVMTI_CLASS_STATUS_ERROR |
8 |
初始化期间的错误使类无法使用 |
JVMTI_CLASS_STATUS_ARRAY |
16 |
类是一个数组。如果设置,所有其他位都为零。 |
JVMTI_CLASS_STATUS_PRIMITIVE |
32 |
类是原始类(例如 java.lang.Integer.TYPE )。如果设置,所有其他位都为零。 |
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
status_ptr |
jint* |
返回时,指向此类的当前状态作为 类状态标志 中的一个或多个。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
获取源文件名
jvmtiError
GetSourceFileName(jvmtiEnv* env,
jclass klass,
char** source_name_ptr)
对于 klass
指示的类,通过 source_name_ptr
返回源文件名。返回的字符串只是一个文件名,从不包含目录名。
对于原始类(例如 java.lang.Integer.TYPE
)和数组,此函数返回 JVMTI_ERROR_ABSENT_INFORMATION
。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
source_name_ptr |
char** |
返回时,指向类的源文件名,编码为 修改后的 UTF-8 字符串。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。 |
获取类修饰符
jvmtiError
GetClassModifiers(jvmtiEnv* env,
jclass klass,
jint* modifiers_ptr)
对于 klass
指示的类,通过 modifiers_ptr
返回访问标志。访问标志定义在Java™ 虚拟机规范,第 4 章.
如果该类是数组类,那么它的public、private 和protected 修饰符与它的组件类型相同。对于基元数组,此组件类型由基元类之一(例如 java.lang.Integer.TYPE
)表示。
如果该类是原始类,它的 public 修饰符总是 true,它的 protected 和 private修饰符总是 false。
如果该类是数组类或原始类,则其最终修饰符始终为真,其接口修饰符始终为假。它的其他修饰符的值不由本规范确定。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
modifiers_ptr |
jint* |
返回时,指向此类的当前访问标志。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
获取类方法
jvmtiError
GetClassMethods(jvmtiEnv* env,
jclass klass,
jint* method_count_ptr,
jmethodID** methods_ptr)
对于 klass
指示的类,通过 method_count_ptr
返回方法计数,通过 methods_ptr
返回方法 ID 列表。方法列表包含构造函数和静态初始值设定项以及真正的方法。仅返回直接声明的方法(不返回继承的方法)。为数组类和基本类(例如 java.lang.Integer.TYPE
)返回一个空方法列表。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
method_count_ptr |
jint* |
返回时,指向此类中声明的方法数。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
methods_ptr |
jmethodID** |
返回时,指向方法 ID 数组。
Agent 传递一个指向 jmethodID* 的指针。返回时,jmethodID* 指向新分配的大小为 *method_count_ptr 的数组。该数组应使用 Deallocate 释放。 |
获取类字段
jvmtiError
GetClassFields(jvmtiEnv* env,
jclass klass,
jint* field_count_ptr,
jfieldID** fields_ptr)
对于 klass
指示的类,通过 field_count_ptr
返回字段计数,通过 fields_ptr
返回字段 ID 列表。仅返回直接声明的字段(不返回继承的字段)。字段按照它们在类文件中出现的顺序返回。为数组类和原始类(例如 java.lang.Integer.TYPE
)返回一个空字段列表。使用 JNI 确定数组的长度。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
field_count_ptr |
jint* |
返回时,指向此类中声明的字段数。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
fields_ptr |
jfieldID** |
返回时,指向字段 ID 数组。
Agent 传递一个指向 jfieldID* 的指针。返回时,jfieldID* 指向新分配的大小为 *field_count_ptr 的数组。该数组应使用 Deallocate 释放。 |
获取已实现的接口
jvmtiError
GetImplementedInterfaces(jvmtiEnv* env,
jclass klass,
jint* interface_count_ptr,
jclass** interfaces_ptr)
返回此类的直接超级接口。对于类,此函数返回在其 implements
子句中声明的接口。对于接口,此函数返回在其 extends
子句中声明的接口。为数组类和原始类(例如 java.lang.Integer.TYPE
)返回一个空接口列表。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
interface_count_ptr |
jint* |
返回时,指向接口的数量。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
interfaces_ptr |
jclass** |
返回时,指向接口数组。
Agent 传递一个指向 jclass* 的指针。返回时,jclass* 指向新分配的大小为 *interface_count_ptr 的数组。该数组应使用 Deallocate 释放。 interfaces_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
获取类版本号
jvmtiError
GetClassVersionNumbers(jvmtiEnv* env,
jclass klass,
jint* minor_version_ptr,
jint* major_version_ptr)
对于 klass
指示的类,返回次要和主要版本号,如中所定义Java™ 虚拟机规范,第 4 章.
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
minor_version_ptr |
jint* |
返回时,指向类文件格式的 minor_version 项的值。注意:为了与Class File Format保持一致,次版本号为第一个参数。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
major_version_ptr |
jint* |
返回时,指向类文件格式的major_version 项的值。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
获取常量池
jvmtiError
GetConstantPool(jvmtiEnv* env,
jclass klass,
jint* constant_pool_count_ptr,
jint* constant_pool_byte_count_ptr,
unsigned char** constant_pool_bytes_ptr)
对于klass
指示的类,以constant_pool
项的格式返回常量池的原始字节Java™ 虚拟机规范,第 4 章.常量池的格式可能因类文件格式的版本而异,因此,应检查 次要和主要类版本号 的兼容性。
返回的常量池可能与定义类文件中的常量池具有不同的布局或内容。 GetConstantPool() 返回的常量池可能比定义的常量池具有更多或更少的条目。条目的顺序可能不同。 GetConstantPool() 返回的常量池将匹配 GetBytecodes() 使用的常量池。也就是说,GetBytecodes() 返回的字节码将具有常量池索引,这些索引引用 GetConstantPool() 返回的常量池条目。注意,由于RetransformClasses
和RedefineClasses
可以改变常量池,所以这个函数返回的常量池也可以随之改变。因此,如果存在中间类重新转换或重新定义,则 GetConstantPool() 和 GetBytecodes() 之间的对应关系不成立。给定字节码使用的常量池条目的值将与定义类文件的值匹配(即使索引不匹配)。不被字节码直接或间接使用的常量池条目(例如,与注释关联的 UTF-8 字符串)不需要存在于返回的常量池中。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
constant_pool_count_ptr |
jint* |
返回时,指向常量池表中的条目数加一。这对应于类文件格式的constant_pool_count 项。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
constant_pool_byte_count_ptr |
jint* |
返回时,指向返回的原始常量池中的字节数。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
constant_pool_bytes_ptr |
unsigned char** |
返回时,指向原始常量池,即类文件格式的 constant_pool 项定义的字节
Agent 传递一个指向 unsigned char* 的指针。返回时,unsigned char* 指向新分配的大小为 *constant_pool_byte_count_ptr 的数组。该数组应使用 Deallocate 释放。 |
是接口
jvmtiError
IsInterface(jvmtiEnv* env,
jclass klass,
jboolean* is_interface_ptr)
确定类对象引用是否表示接口。如果“类”实际上是一个接口,则 jboolean
结果为 JNI_TRUE
,否则为 JNI_FALSE
。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
is_interface_ptr |
jboolean* |
返回时,指向此函数的布尔结果。
Agent 传递一个指向 jboolean 的指针。返回时,jboolean 已设置。 |
是数组类
jvmtiError
IsArrayClass(jvmtiEnv* env,
jclass klass,
jboolean* is_array_class_ptr)
确定类对象引用是否表示数组。如果类是数组,则 jboolean
结果为 JNI_TRUE
,否则为 JNI_FALSE
。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
is_array_class_ptr |
jboolean* |
返回时,指向此函数的布尔结果。
Agent 传递一个指向 jboolean 的指针。返回时,jboolean 已设置。 |
是可修改类
jvmtiError
IsModifiableClass(jvmtiEnv* env,
jclass klass,
jboolean* is_modifiable_class_ptr)
确定类是否可修改。如果类是可修改的(is_modifiable_class_ptr
返回 JNI_TRUE
),则可以使用 RedefineClasses
重新定义该类(假设代理拥有 can_redefine_classes
能力)或使用 RetransformClasses
重新转换(假设代理拥有 can_retransform_classes
能力)。如果类不可修改(is_modifiable_class_ptr
返回 JNI_FALSE
),则该类既不能重新定义也不能重新转换。
原始类(例如 java.lang.Integer.TYPE
)、数组类和一些实现定义的类永远不可修改。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
is_modifiable_class_ptr |
jboolean* |
返回时,指向此函数的布尔结果。
Agent 传递一个指向 jboolean 的指针。返回时,jboolean 已设置。 |
获取类加载器
jvmtiError
GetClassLoader(jvmtiEnv* env,
jclass klass,
jobject* classloader_ptr)
对于 klass
指示的类,通过 classloader_ptr
返回对该类的类加载器的引用。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
classloader_ptr |
jobject* |
返回时,指向加载此类的类加载器。如果类不是由类加载器创建的,或者如果类加载器是引导类加载器,则指向 NULL 。
代理传递一个指向 jobject 的指针。返回时,jobject 已设置。 classloader_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
获取源调试扩展
jvmtiError
GetSourceDebugExtension(jvmtiEnv* env,
jclass klass,
char** source_debug_extension_ptr)
对于 klass
指示的类,通过 source_debug_extension_ptr
返回调试扩展。返回的字符串恰好包含 klass
的类文件中存在的调试扩展信息。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
source_debug_extension_ptr |
char** |
返回时,指向类的调试扩展,编码为 修改后的 UTF-8 字符串。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。 |
重新转换类
jvmtiError
RetransformClasses(jvmtiEnv* env,
jint class_count,
const jclass* classes)
此功能有助于 字节码检测 已加载的类。要在不引用现有字节码的情况下替换类定义,就像从源代码重新编译以进行修复并继续调试时可能会做的那样,应该使用 RedefineClasses
函数。
当最初加载类或它们是 redefined 时,可以使用 ClassFileLoadHook
事件转换初始类文件字节。此函数重新运行转换过程(无论之前是否发生过转换)。此重新转换遵循以下步骤:
- 从初始类文件字节开始
- 对于在先前加载或重新定义期间接收到
ClassFileLoadHook
事件的每个 无法再改造 代理,它返回的字节(通过 new_class_data
参数)被重新用作转换的输出;请注意,这相当于重新应用之前的转换,没有改变。除了ClassFileLoadHook
事件是不是发送给这些代理
- 对于每个 再转化能力 代理,发送
ClassFileLoadHook
事件,允许应用新的转换
- 转换后的类文件字节作为类的新定义安装
有关详细信息,请参阅 ClassFileLoadHook
事件。
初始类文件字节表示传递给 ClassLoader.defineClass
或 RedefineClasses
的字节(在应用任何转换之前),但是它们可能不完全匹配。常量池可能在 GetConstantPool
中描述的方式有所不同。方法字节码中的常量池索引将对应。某些属性可能不存在。如果顺序没有意义,例如方法的顺序,则可能不会保留顺序。
重新转换可能导致安装新版本的方法。旧方法版本可能变为 obsolete 新方法版本将用于新调用。如果一个方法有活跃的堆栈帧,那些活跃的帧继续运行原始方法版本的字节码。
这个函数不会导致任何初始化,除非在通常的 JVM 语义下会发生初始化。换句话说,重新转换类不会导致其初始化程序运行。静态字段的值将保持在调用之前的状态。
线程不需要暂停。
清除类中的所有断点。
所有属性都已更新。
重新转换类的实例不受影响——字段保留它们以前的值。实例上的 标签 也不受影响。
为响应此调用,将发送 ClassFileLoadHook
事件以外的任何事件。
重新转换可能会更改方法体、常量池和属性(除非明确禁止)。重新转换不得添加、删除或重命名字段或方法、更改方法的签名、更改修饰符或更改继承。重新转换不得更改 NestHost
、NestMembers
、Record
或 PermittedSubclasses
属性。这些限制可能会在未来的版本中取消。如果尝试进行不受支持的重新转换,请参阅下面的错误返回说明,了解有关返回的错误代码的信息。类文件字节在通过 ClassFileLoadHook
事件链之前不会被验证或安装,因此返回的错误代码反映了转换的结果。如果返回 JVMTI_ERROR_NONE
以外的任何错误代码,则所有要重新转换的类都不会安装新定义。当此函数返回时(错误代码为 JVMTI_ERROR_NONE
),所有要重新转换的类都将安装其新定义。
参数
Name |
Type |
Description |
class_count |
jint |
要重新转换的类数。 |
classes |
const jclass* |
要重新转换的类数组。
代理传入 jclass 的 class_count 个元素的数组。 |
重新定义类
typedef struct {
jclass klass;
jint class_byte_count;
const unsigned char* class_bytes;
} jvmtiClassDefinition;
jvmtiError
RedefineClasses(jvmtiEnv* env,
jint class_count,
const jvmtiClassDefinition* class_definitions)
根据提供的定义重新定义所有给定的类。此函数用于用新定义替换类的定义,这在修复并继续调试中可能需要。在要转换现有类文件字节的地方,例如在 字节码检测 中,应使用 RetransformClasses
。
重新定义可能导致安装新版本的方法。旧方法版本可能变为 obsolete 新方法版本将用于新调用。如果一个方法有活跃的堆栈帧,那些活跃的帧继续运行原始方法版本的字节码。如果需要重置堆栈帧,请使用 PopFrame
弹出具有过时方法版本的帧。
这个函数不会导致任何初始化,除非在通常的 JVM 语义下会发生初始化。换句话说,重新定义一个类不会导致其初始化程序运行。静态字段的值将保持在调用之前的状态。
线程不需要暂停。
清除类中的所有断点。
所有属性都已更新。
重新定义类的实例不受影响——字段保留它们以前的值。实例上的 标签 也不受影响。
为了响应这个号召,JVM 将发送 TI 事件 类文件加载挂钩(如果启用),但不会发送其他 JVM 将发送 TI 事件。
重新定义可能会改变方法体、常量池和属性(除非明确禁止)。重新定义不得添加、删除或重命名字段或方法、更改方法的签名、更改修饰符或更改继承。重新定义不得更改 NestHost
、NestMembers
、Record
或 PermittedSubclasses
属性。这些限制可能会在未来的版本中取消。如果尝试进行不受支持的重新定义,请参阅下面的错误返回说明,了解有关返回的错误代码的信息。类文件字节在通过 ClassFileLoadHook
事件链之前不会被验证或安装,因此返回的错误代码反映了应用于传递到 class_definitions
的字节的转换结果。如果返回 JVMTI_ERROR_NONE
以外的任何错误代码,则将不会安装任何要重新定义的类的新定义。当此函数返回时(错误代码为 JVMTI_ERROR_NONE
),将安装所有要重新定义的类的新定义。
jvmtiClassDefinition
- 类重定义说明
Field |
Type |
Description |
klass |
jclass |
此类的类对象 |
class_byte_count |
jint |
定义类的字节数(下) |
class_bytes |
const unsigned char* |
定义类的字节(在Java™ 虚拟机规范,第 4 章) |
参数
Name |
Type |
Description |
class_count |
jint |
class_definitions 中指定的类数 |
class_definitions |
const jvmtiClassDefinition* |
新类定义的数组
代理传入 jvmtiClassDefinition 的 class_count 个元素的数组。 |
Object
对象函数:
对象类型:
获取对象大小
jvmtiError
GetObjectSize(jvmtiEnv* env,
jobject object,
jlong* size_ptr)
对于 object
指示的对象,通过 size_ptr
返回对象的大小。此大小是此对象消耗的存储量的特定于实现的近似值。它可能包括对象的部分或全部开销,因此对于实现内的比较很有用,但对实现之间的比较没有用。估计值可能会在 JVM 的单次调用期间发生变化。
参数
Name |
Type |
Description |
object |
jobject |
要查询的对象。 |
size_ptr |
jlong* |
返回时,指向对象的字节大小。
Agent 传递一个指向 jlong 的指针。返回时,jlong 已设置。 |
获取对象哈希码
jvmtiError
GetObjectHashCode(jvmtiEnv* env,
jobject object,
jint* hash_code_ptr)
对于 object
指示的对象,通过 hash_code_ptr
返回一个哈希码。此哈希码可用于维护对象引用的哈希表,但是,在某些实现中,这可能会对性能产生重大影响——在大多数情况下,标签 将是将信息与对象相关联的更有效方法。此函数保证特定对象在其整个生命周期中具有相同的哈希码值
参数
Name |
Type |
Description |
object |
jobject |
要查询的对象。 |
hash_code_ptr |
jint* |
返回时,指向对象的哈希码。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
获取对象监视器使用情况
typedef struct {
jthread owner;
jint entry_count;
jint waiter_count;
jthread* waiters;
jint notify_waiter_count;
jthread* notify_waiters;
} jvmtiMonitorUsage;
jvmtiError
GetObjectMonitorUsage(jvmtiEnv* env,
jobject object,
jvmtiMonitorUsage* info_ptr)
获取有关对象监视器的信息。 jvmtiMonitorUsage
结构的字段填充了有关监视器使用情况的信息。
jvmtiMonitorUsage
- 对象监视器使用信息
Field |
Type |
Description |
owner |
jthread |
拥有此监视器的线程,如果未使用则为 NULL |
entry_count |
jint |
所属线程进入管程的次数 |
waiter_count |
jint |
等待拥有此监视器的线程数 |
waiters |
jthread* |
waiter_count 等待线程 |
notify_waiter_count |
jint |
等待此监视器通知的线程数 |
notify_waiters |
jthread* |
等待通知的 notify_waiter_count 个线程 |
参数
Name |
Type |
Description |
object |
jobject |
要查询的对象。 |
info_ptr |
jvmtiMonitorUsage* |
返回时,填充指定对象的监视器信息。
Agent 传递一个指向 jvmtiMonitorUsage 的指针。返回时,jvmtiMonitorUsage 已设置。 jvmtiMonitorUsage 的 owner 字段返回的对象是 JNI 本地引用,必须是 管理 。 jvmtiMonitorUsage 的字段waiters 返回的指针是一个新分配的数组。该数组应使用 Deallocate 释放。 jvmtiMonitorUsage 的 waiters 字段中返回的对象是 JNI 本地引用,必须是 管理 。 jvmtiMonitorUsage 的字段notify_waiters 返回的指针是一个新分配的数组。该数组应使用 Deallocate 释放。 jvmtiMonitorUsage 的 notify_waiters 字段中返回的对象是 JNI 本地引用,必须是 管理 。 |
Field
现场功能:
获取字段名称(和签名)
jvmtiError
GetFieldName(jvmtiEnv* env,
jclass klass,
jfieldID field,
char** name_ptr,
char** signature_ptr,
char** generic_ptr)
对于 klass
和 field
指示的字段,通过 name_ptr
返回字段名称,通过 signature_ptr
返回字段签名。
字段签名在 JNI规范 中定义,在中称为 field descriptors
Java™ 虚拟机规范,第 4.3.2 章.
参数
Name |
Type |
Description |
klass |
jclass |
要查询的字段的类。 |
field |
jfieldID |
要查询的字段。 |
name_ptr |
char ** |
返回时,指向字段名称,编码为 修改后的 UTF-8 字符串。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。如果 name_ptr 是 NULL ,则不返回名称。 |
signature_ptr |
char ** |
返回时,指向字段签名,编码为 修改后的 UTF-8 字符串。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。如果 signature_ptr 是 NULL ,则不返回签名。 |
generic_ptr |
char ** |
返回时,指向字段的通用签名,编码为 修改后的 UTF-8 字符串。如果该字段没有通用签名属性,则在返回时指向 NULL 。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。如果 generic_ptr 是 NULL ,则不返回通用签名。 |
获取字段声明类
jvmtiError
GetFieldDeclaringClass(jvmtiEnv* env,
jclass klass,
jfieldID field,
jclass* declaring_class_ptr)
对于 klass
和 field
指示的字段,返回通过 declaring_class_ptr
定义它的类。声明类将是 klass
、超类或已实现的接口。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
field |
jfieldID |
要查询的字段。 |
declaring_class_ptr |
jclass* |
返回时,指向声明类
代理将指针传递给 jclass 。返回时,jclass 已设置。 declaring_class_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
获取字段修饰符
jvmtiError
GetFieldModifiers(jvmtiEnv* env,
jclass klass,
jfieldID field,
jint* modifiers_ptr)
对于 klass
和 field
指示的字段,通过 modifiers_ptr
返回访问标志。访问标志定义在Java™ 虚拟机规范,第 4 章.
参数
Name |
Type |
Description |
klass |
jclass |
要查询的类。 |
field |
jfieldID |
要查询的字段。 |
modifiers_ptr |
jint* |
返回时,指向访问标志。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
是场合成
jvmtiError
IsFieldSynthetic(jvmtiEnv* env,
jclass klass,
jfieldID field,
jboolean* is_synthetic_ptr)
对于 klass
和 field
指示的字段,返回一个值,指示该字段是否是通过 is_synthetic_ptr
合成的。合成字段由编译器生成,但不存在于原始源代码中。
参数
Name |
Type |
Description |
klass |
jclass |
要查询的字段的类。 |
field |
jfieldID |
要查询的字段。 |
is_synthetic_ptr |
jboolean* |
返回时,指向此函数的布尔结果。
Agent 传递一个指向 jboolean 的指针。返回时,jboolean 已设置。 |
Method
方法功能:
方法类型:
这些函数提供有关方法的信息(表示为 jmethodID
)并设置方法的处理方式。
过时的方法
RetransformClasses
和 RedefineClasses
函数可以导致安装新版本的方法。在以下情况下,方法的原始版本被认为等同于新版本:
与新方法版本不等同的原始方法版本称为过时的,并分配一个新的方法ID;原始方法 ID 现在指的是新方法版本。可以使用 IsMethodObsolete
测试方法 ID 是否过时。
获取方法名称(和签名)
jvmtiError
GetMethodName(jvmtiEnv* env,
jmethodID method,
char** name_ptr,
char** signature_ptr,
char** generic_ptr)
对于method
指示的方法,通过name_ptr
返回方法名,通过signature_ptr
返回方法签名。
方法签名在 JNI规范 中定义,在中称为 method descriptors
Java™ 虚拟机规范,第 4.3.3 章.请注意,这与定义在Java 语言规范.
参数
Name |
Type |
Description |
method |
jmethodID |
查询的方法。 |
name_ptr |
char ** |
返回时,指向方法名称,编码为 修改后的 UTF-8 字符串。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。如果 name_ptr 是 NULL ,则不返回名称。 |
signature_ptr |
char ** |
返回时,指向方法签名,编码为 修改后的 UTF-8 字符串。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。如果 signature_ptr 是 NULL ,则不返回签名。 |
generic_ptr |
char ** |
返回时,指向方法的通用签名,编码为 修改后的 UTF-8 字符串。如果该方法没有通用签名属性,则在返回时指向 NULL 。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。如果 generic_ptr 是 NULL ,则不返回通用签名。 |
获取方法声明类
jvmtiError
GetMethodDeclaringClass(jvmtiEnv* env,
jmethodID method,
jclass* declaring_class_ptr)
对于 method
指示的方法,通过 declaring_class_ptr
返回定义它的类。
参数
Name |
Type |
Description |
method |
jmethodID |
查询的方法。 |
declaring_class_ptr |
jclass* |
返回时,指向声明类
代理将指针传递给 jclass 。返回时,jclass 已设置。 declaring_class_ptr 返回的对象是 JNI 本地引用,必须是 管理。 |
获取方法修饰符
jvmtiError
GetMethodModifiers(jvmtiEnv* env,
jmethodID method,
jint* modifiers_ptr)
对于 method
指示的方法,通过 modifiers_ptr
返回访问标志。访问标志定义在Java™ 虚拟机规范,第 4 章.
参数
Name |
Type |
Description |
method |
jmethodID |
查询的方法。 |
modifiers_ptr |
jint* |
返回时,指向访问标志。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
获得最大当地人
jvmtiError
GetMaxLocals(jvmtiEnv* env,
jmethodID method,
jint* max_ptr)
对于 method
指示的方法,返回该方法使用的局部变量槽的数量,包括调用时用于向方法传递参数的局部变量。
参见 max_locals
Java™ 虚拟机规范,第 4.7.3 章.
参数
Name |
Type |
Description |
method |
jmethodID |
查询的方法。 |
max_ptr |
jint* |
返回时,指向本地插槽的最大数量
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
获取参数大小
jvmtiError
GetArgumentsSize(jvmtiEnv* env,
jmethodID method,
jint* size_ptr)
对于 method
指示的方法,通过 max_ptr
返回方法参数使用的局部变量槽的数量。请注意,双词参数使用两个槽。
参数
Name |
Type |
Description |
method |
jmethodID |
查询的方法。 |
size_ptr |
jint* |
返回时,指向参数槽的数量
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
获取行号表
typedef struct {
jlocation start_location;
jint line_number;
} jvmtiLineNumberEntry;
jvmtiError
GetLineNumberTable(jvmtiEnv* env,
jmethodID method,
jint* entry_count_ptr,
jvmtiLineNumberEntry** table_ptr)
对于 method
指示的方法,返回源行号条目表。表的大小通过 entry_count_ptr
返回,表本身通过 table_ptr
返回。
jvmtiLineNumberEntry
- 行号表条目
Field |
Type |
Description |
start_location |
jlocation |
该行开始的 jlocation |
line_number |
jint |
行号 |
参数
Name |
Type |
Description |
method |
jmethodID |
查询的方法。 |
entry_count_ptr |
jint* |
返回时,指向表中的条目数
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
table_ptr |
jvmtiLineNumberEntry** |
返回时,指向行号表指针。
Agent 传递一个指向 jvmtiLineNumberEntry* 的指针。返回时,jvmtiLineNumberEntry* 指向新分配的大小为 *entry_count_ptr 的数组。该数组应使用 Deallocate 释放。 |
获取方法位置
jvmtiError
GetMethodLocation(jvmtiEnv* env,
jmethodID method,
jlocation* start_location_ptr,
jlocation* end_location_ptr)
对于method
表示的方法,通过start_location_ptr
和end_location_ptr
返回起止地址。在传统的字节码索引方案中,start_location_ptr
将始终指向零,end_location_ptr
将始终指向字节码计数减一。
获取局部变量表
typedef struct {
jlocation start_location;
jint length;
char* name;
char* signature;
char* generic_signature;
jint slot;
} jvmtiLocalVariableEntry;
jvmtiError
GetLocalVariableTable(jvmtiEnv* env,
jmethodID method,
jint* entry_count_ptr,
jvmtiLocalVariableEntry** table_ptr)
返回局部变量信息。
jvmtiLocalVariableEntry
- 局部变量表条目
Field |
Type |
Description |
start_location |
jlocation |
局部变量首先有效的代码数组索引(即必须有值的位置)。 |
length |
jint |
此局部变量的有效部分的长度。局部变量有效的最后一个代码数组索引是 start_location + length 。 |
name |
char* |
局部变量名称,编码为 修改后的 UTF-8 字符串。 |
signature |
char* |
局部变量的类型签名,编码为 修改后的 UTF-8 字符串。签名格式与定义的相同Java™ 虚拟机规范,第 4.3.2 章. |
generic_signature |
char* |
局部变量的通用签名,编码为 修改后的 UTF-8 字符串。对于没有通用类型的任何局部变量,此字段的值将为 NULL 。 |
slot |
jint |
局部变量的槽。参见 局部变量。 |
参数
Name |
Type |
Description |
method |
jmethodID |
查询的方法。 |
entry_count_ptr |
jint* |
返回时,指向表中的条目数
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
table_ptr |
jvmtiLocalVariableEntry** |
返回时,指向局部变量表条目的数组。
Agent 传递一个指向 jvmtiLocalVariableEntry* 的指针。返回时,jvmtiLocalVariableEntry* 指向新分配的大小为 *entry_count_ptr 的数组。该数组应使用 Deallocate 释放。 jvmtiLocalVariableEntry 的name 字段中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 jvmtiLocalVariableEntry 的signature 字段中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 jvmtiLocalVariableEntry 的generic_signature 字段中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 |
获取字节码
jvmtiError
GetBytecodes(jvmtiEnv* env,
jmethodID method,
jint* bytecode_count_ptr,
unsigned char** bytecodes_ptr)
对于 method
指示的方法,返回实现该方法的字节码。字节码的数量通过 bytecode_count_ptr
返回。字节码本身通过 bytecodes_ptr
返回。
参数
Name |
Type |
Description |
method |
jmethodID |
查询的方法。 |
bytecode_count_ptr |
jint* |
返回时,指向字节码数组的长度
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
bytecodes_ptr |
unsigned char** |
返回时,指向字节码数组的指针
Agent 传递一个指向 unsigned char* 的指针。返回时,unsigned char* 指向新分配的大小为 *bytecode_count_ptr 的数组。该数组应使用 Deallocate 释放。 |
是本地方法
jvmtiError
IsMethodNative(jvmtiEnv* env,
jmethodID method,
jboolean* is_native_ptr)
对于method
表示的方法,通过is_native_ptr
返回一个值表示该方法是否是native
参数
Name |
Type |
Description |
method |
jmethodID |
查询的方法。 |
is_native_ptr |
jboolean* |
返回时,指向此函数的布尔结果。
Agent 传递一个指向 jboolean 的指针。返回时,jboolean 已设置。 |
方法是合成的
jvmtiError
IsMethodSynthetic(jvmtiEnv* env,
jmethodID method,
jboolean* is_synthetic_ptr)
对于 method
指示的方法,返回一个值表示该方法是否是通过 is_synthetic_ptr
合成的。合成方法由编译器生成,但不存在于原始源代码中。
参数
Name |
Type |
Description |
method |
jmethodID |
查询的方法。 |
is_synthetic_ptr |
jboolean* |
返回时,指向此函数的布尔结果。
Agent 传递一个指向 jboolean 的指针。返回时,jboolean 已设置。 |
方法是否过时
jvmtiError
IsMethodObsolete(jvmtiEnv* env,
jmethodID method,
jboolean* is_obsolete_ptr)
确定方法 ID 是否引用 obsolete 方法版本。
参数
Name |
Type |
Description |
method |
jmethodID |
要查询的方法 ID。 |
is_obsolete_ptr |
jboolean* |
返回时,指向此函数的布尔结果。
Agent 传递一个指向 jboolean 的指针。返回时,jboolean 已设置。 |
设置本机方法前缀
jvmtiError
SetNativeMethodPrefix(jvmtiEnv* env,
const char* prefix)
此函数通过允许使用应用于名称的前缀重试来修改本地方法解析的失败处理。当与 ClassFileLoadHook 事件 一起使用时,它使本机方法成为 instrumented 。
由于不能直接检测本地方法(它们没有字节码),因此必须用可以检测的非本地方法包装它们。例如,如果我们有:
native boolean foo(int x);
我们可以转换类文件(使用 ClassFileLoadHook 事件),使其变为:
boolean foo(int x) {
... record entry to foo ...
return wrapped_foo(x);
}
native boolean wrapped_foo(int x);
其中 foo 成为带有附加前缀“wrapped_”的实际本地方法的包装器。请注意,“wrapped_”是一个糟糕的前缀选择,因为它可能构成现有方法的名称,因此类似“$$$MyAgentWrapped$$$_”的名称会更好,但会使这些示例的可读性降低。
包装器将允许在本地方法调用时收集数据,但现在问题变成了将包装方法与本地实现链接起来。也就是说,方法 wrapped_foo
需要解析为 foo
的本机实现,它可能是:
Java_somePackage_someClass_foo(JNIEnv* env, jint x)
此函数允许指定前缀并进行正确的解析。具体来说,当标准解析失败时,将考虑前缀重试解析。解析有两种方式,使用 JNI 函数 RegisterNatives
的显式解析和正常的自动解析。对于 RegisterNatives
,VM 将尝试此关联:
method(foo) -> nativeImplementation(foo)
如果失败,将在方法名称前加上指定的前缀重试解析,从而产生正确的解析:
method(wrapped_foo) -> nativeImplementation(foo)
对于自动解析,VM 将尝试:
method(wrapped_foo) -> nativeImplementation(wrapped_foo)
如果失败,将使用从实现名称中删除的指定前缀重试解析,从而产生正确的解析:
method(wrapped_foo) -> nativeImplementation(foo)
请注意,由于前缀仅在标准解析失败时使用,因此可以选择性地包装本地方法。
由于每个 JVM TI 环境是独立的,可以自己转换字节码,可以应用不止一层的包装器。因此每个环境都需要自己的前缀。由于转换是按顺序应用的,因此前缀(如果应用)将以相同的顺序应用。 ClassFileLoadHook
事件中描述了转换应用程序的顺序。因此,如果三个环境应用了包装器,foo
可能会变成 $env3_$env2_$env1_foo
。但是,比方说,如果第二个环境没有对 foo
应用包装器,它就只是 $env3_$env1_foo
。为了能够有效地确定前缀序列,只有在存在非本地包装器时才应用中间前缀。因此,在最后一个示例中,即使 $env1_foo
不是本机方法,也会应用 $env1_
前缀,因为 $env1_foo
存在。
由于前缀是在解析时使用的,并且解析可能会被任意延迟,因此只要存在相应的带前缀的本机方法,本机方法前缀就必须保持设置。
参数
Name |
Type |
Description |
prefix |
const char * |
要应用的前缀,编码为 修改后的 UTF-8 字符串。
代理传入一个 char 数组。如果 prefix 是 NULL ,则取消此环境中的任何现有前缀。 |
设置本机方法前缀
jvmtiError
SetNativeMethodPrefixes(jvmtiEnv* env,
jint prefix_count,
char** prefixes)
对于普通代理,SetNativeMethodPrefix
将提供所有需要的本地方法前缀。对于执行多个独立类文件转换的元代理(例如,作为另一层代理的代理),此函数允许每个转换都有自己的前缀。前缀按照提供的顺序应用,并按照与从多个 JVM 应用前缀所描述的相同方式进行处理 SetNativeMethodPrefix
中的 TI 环境。
任何先前的前缀都将被替换。因此,使用 prefix_count
of 0
调用此函数会禁用此环境中的前缀。
SetNativeMethodPrefix
和这个函数是设置前缀的两种方式。使用前缀调用 SetNativeMethodPrefix
与使用 1
的 prefix_count
调用此函数相同。用 NULL
调用 SetNativeMethodPrefix
与用 0
的 prefix_count
调用这个函数是一样的。
参数
Name |
Type |
Description |
prefix_count |
jint |
要应用的前缀数。 |
prefixes |
char ** |
应用于此环境的前缀,每个都编码为 修改后的 UTF-8 字符串。
|
原始监视器
原始监视器功能:
创建原始监视器
jvmtiError
CreateRawMonitor(jvmtiEnv* env,
const char* name,
jrawMonitorID* monitor_ptr)
创建一个原始监视器。
只能在 OnLoad 或 live 阶段调用
31
1.0
参数
Name |
Type |
Description |
name |
const char* |
标识监视器的名称,编码为 修改后的 UTF-8 字符串。
代理传入一个 char 数组。 |
monitor_ptr |
jrawMonitorID* |
返回时,指向创建的监视器。
Agent 传递一个指向 jrawMonitorID 的指针。返回时,jrawMonitorID 已设置。 |
销毁原始监视器
jvmtiError
DestroyRawMonitor(jvmtiEnv* env,
jrawMonitorID monitor)
销毁原始监视器。如果被销毁的monitor已经被该线程进入过,则在销毁前先退出。如果被销毁的monitor已经被其他线程进入,则返回错误,monitor不会被销毁。
只能在 OnLoad 或 live 阶段调用
32
1.0
原始监视器输入
jvmtiError
RawMonitorEnter(jvmtiEnv* env,
jrawMonitorID monitor)
获得原始监视器的独家所有权。同一个线程可能会多次进入监视器。线程必须 exit 监视与输入相同的次数。如果在 OnLoad
期间(在附加线程存在之前)进入监视器并且在附加线程存在时没有退出,则认为进入发生在主线程上。
原始监视器退出
jvmtiError
RawMonitorExit(jvmtiEnv* env,
jrawMonitorID monitor)
释放原始监视器的独占所有权。
原始监视器等待
jvmtiError
RawMonitorWait(jvmtiEnv* env,
jrawMonitorID monitor,
jlong millis)
等待原始监视器的通知。
导致当前线程等待,直到另一个线程为指定的原始监视器调用 RawMonitorNotify
或 RawMonitorNotifyAll
,或者指定的 timeout 已经过去。
参数
Name |
Type |
Description |
monitor |
jrawMonitorID |
显示器 |
millis |
jlong |
超时时间,以毫秒为单位。如果超时为零,则不考虑实时,线程只是等待直到收到通知。 |
原始监视器通知
jvmtiError
RawMonitorNotify(jvmtiEnv* env,
jrawMonitorID monitor)
通知在原始监视器上等待的单个线程。
原始监视器通知所有
jvmtiError
RawMonitorNotifyAll(jvmtiEnv* env,
jrawMonitorID monitor)
通知在原始监视器上等待的所有线程。
JNI 函数拦截
JNI 函数拦截函数:
通过操纵 JNI 函数表提供拦截和重新发送 Java 本机接口 (JNI) 函数调用的能力。请参阅 JNI 函数Java 本机接口规范.
以下示例说明拦截 NewGlobalRef
JNI 调用以计算引用创建。
JNIEnv original_jni_Functions;
JNIEnv redirected_jni_Functions;
int my_global_ref_count = 0;
jobject
MyNewGlobalRef(JNIEnv *jni_env, jobject lobj) {
++my_global_ref_count;
return originalJNIFunctions->NewGlobalRef(env, lobj);
}
void
myInit() {
jvmtiError err;
err = (*jvmti_env)->GetJNIFunctionTable(jvmti_env, &original_jni_Functions);
if (err != JVMTI_ERROR_NONE) {
die();
}
err = (*jvmti_env)->GetJNIFunctionTable(jvmti_env, &redirected_jni_Functions);
if (err != JVMTI_ERROR_NONE) {
die();
}
redirectedJNIFunctions->NewGlobalRef = MyNewGlobalRef;
err = (*jvmti_env)->SetJNIFunctionTable(jvmti_env, redirected_jni_Functions);
if (err != JVMTI_ERROR_NONE) {
die();
}
}
在 myInit
被调用后的某个时间,用户的 JNI 代码被执行,这使得调用创建一个新的全局引用。调用转到 myNewGlobalRef
而不是转到正常的 JNI 实现。请注意,保留原始函数表的副本,以便在收集数据后可以调用正常的 JNI 函数。另请注意,任何未被覆盖的 JNI 函数都将正常运行。
设置 JNI 函数表
jvmtiError
SetJNIFunctionTable(jvmtiEnv* env,
const jniNativeInterface* function_table)
在所有当前和未来的 JNI 环境中设置 JNI 函数表。因此,所有未来的 JNI 调用都指向指定的函数。使用 GetJNIFunctionTable
获取要传递给此函数的函数表。要使此功能生效,JNI 客户端必须使用更新的表条目。由于该表是const
定义的,一些编译器可能会优化对表的访问,从而阻止该函数生效。表已复制——对表的本地副本所做的更改无效。此功能仅影响功能表,环境的所有其他方面均不受影响。请参阅示例 多于。
参数
Name |
Type |
Description |
function_table |
const jniNativeInterface * |
指向新的 JNI 函数表。
Agent 传入一个指向 jniNativeInterface 的指针。 |
获取 JNI 函数表
jvmtiError
GetJNIFunctionTable(jvmtiEnv* env,
jniNativeInterface** function_table)
获取 JNI 函数表。 JNI 函数表被复制到分配的内存中。如果调用了SetJNIFunctionTable
,则返回修改后的(不是原来的)函数表。仅复制功能表,不复制环境的其他方面。请参阅示例 多于。
参数
Name |
Type |
Description |
function_table |
jniNativeInterface ** |
返回时,*function_table 指向新分配的 JNI 函数表副本。
Agent 传递一个指向 jniNativeInterface* 的指针。返回时,jniNativeInterface* 指向一个新分配的数组。该数组应使用 Deallocate 释放。 |
事件管理
事件管理功能:
事件管理类型:
设置事件回调
jvmtiError
SetEventCallbacks(jvmtiEnv* env,
const jvmtiEventCallbacks* callbacks,
jint size_of_callbacks)
设置为每个事件调用的函数。回调是通过提供替换函数表来指定的。函数表被复制——对表的本地副本的更改无效。这是一个原子操作,所有回调都是一次性设置的。在调用此函数之前不发送任何事件。当条目为 NULL
或事件超出 size_of_callbacks
时,不会发送任何事件。有关事件的详细信息在本文档中描述之后。事件必须启用并具有回调才能发送——调用此函数和 SetEventNotificationMode
的顺序不会影响结果。
只能在 OnLoad 或 live 阶段调用
不
122
1.0
参数
Name |
Type |
Description |
callbacks |
const jvmtiEventCallbacks * |
新的事件回调。
Agent 传入一个指向 jvmtiEventCallbacks 的指针。如果 callbacks 是 NULL ,删除现有的回调。 |
size_of_callbacks |
jint |
sizeof(jvmtiEventCallbacks) --为了版本兼容性。 |
设置事件通知模式
typedef enum {
JVMTI_ENABLE = 1,
JVMTI_DISABLE = 0
} jvmtiEventMode;
jvmtiError
SetEventNotificationMode(jvmtiEnv* env,
jvmtiEventMode mode,
jvmtiEvent event_type,
jthread event_thread,
...)
控制事件的产生。
事件启用/禁用 (jvmtiEventMode
)
持续的 |
Value |
Description |
JVMTI_ENABLE |
1 |
如果 mode 是 JVMTI_ENABLE ,事件 event_type 将被启用 |
JVMTI_DISABLE |
0 |
如果 mode 是 JVMTI_DISABLE ,事件 event_type 将被禁用 |
如果 event_thread
为 NULL
,则全局启用或禁用该事件;否则,它为特定线程启用或禁用。如果在线程或全局级别启用特定线程,则会为该线程生成一个事件。
有关特定事件的信息,请参阅 以下。
以下事件无法通过此函数在线程级别进行控制。
最初,在线程级别或全局级别均未启用任何事件。
在调用此函数之前,必须具备任何所需的能力(请参阅下面的事件启用能力)。
以下 描述了有关事件的详细信息。
只能在 OnLoad 或 live 阶段调用
不
2
1.0
参数
Name |
Type |
Description |
mode |
jvmtiEventMode |
JVMTI_ENABLE 或 JVMTI_DISABLE |
event_type |
jvmtiEvent |
要控制的事件 |
event_thread |
jthread |
要控制的线程
如果 event_thread 是 NULL ,则事件在全局级别进行控制。 |
... |
... |
为将来扩展 |
生成事件
jvmtiError
GenerateEvents(jvmtiEnv* env,
jvmtiEvent event_type)
生成事件以表示 VM 的当前状态。例如,如果 event_type
是 JVMTI_EVENT_COMPILED_METHOD_LOAD
,将为每个当前编译的方法发送一个 CompiledMethodLoad
事件。不会发送已加载和现在已卸载的方法。之前发送的事件的历史不会影响此函数发送的事件——例如,每次调用此函数时将发送所有当前编译的方法。
当程序执行开始后由于代理附加而可能错过事件时,此功能很有用;此函数生成错过的事件。
尝试执行 Java 编程语言代码或 JNI 函数可能会暂停,直到此函数返回 - 因此不应从发送事件的线程调用它们。只有在发送、处理并返回错过的事件后,此函数才会返回。事件可能在与事件发生所在的线程不同的线程上发送。必须使用 SetEventCallbacks
设置事件的回调,并且必须使用 SetEventNotificationMode
启用事件,否则事件将不会发生。如果 VM 不再具有生成部分或全部请求事件的信息,则不会发送这些事件 - 不会返回任何错误。
仅支持以下事件:
参数
Name |
Type |
Description |
event_type |
jvmtiEvent |
要生成的事件类型。必须是其中之一:
|
扩展机制
扩展机制功能:
扩展机制功能类型:
扩展机制类型:
这些函数允许 JVM TI 实现提供超出本规范中定义的功能和事件。
扩展函数和扩展事件都有参数,每个参数都有从下表中选择的“类型”和“种类”:
扩展函数/事件参数类型 (jvmtiParamTypes
)
持续的 |
Value |
Description |
JVMTI_TYPE_JBYTE |
101 |
Java 编程语言原始类型 - byte 。 JNI 类型 jbyte 。 |
JVMTI_TYPE_JCHAR |
102 |
Java 编程语言原始类型 - char 。 JNI 类型 jchar 。 |
JVMTI_TYPE_JSHORT |
103 |
Java 编程语言原始类型 - short 。 JNI 类型 jshort 。 |
JVMTI_TYPE_JINT |
104 |
Java 编程语言原始类型 - int 。 JNI 类型 jint 。 |
JVMTI_TYPE_JLONG |
105 |
Java 编程语言原始类型 - long 。 JNI 类型 jlong 。 |
JVMTI_TYPE_JFLOAT |
106 |
Java 编程语言原始类型 - float 。 JNI 类型 jfloat 。 |
JVMTI_TYPE_JDOUBLE |
107 |
Java 编程语言原始类型 - double 。 JNI 类型 jdouble 。 |
JVMTI_TYPE_JBOOLEAN |
108 |
Java 编程语言原始类型 - boolean 。 JNI 类型 jboolean 。 |
JVMTI_TYPE_JOBJECT |
109 |
Java 编程语言对象类型 - java.lang.Object 。 JNI 类型 jobject 。返回值是 JNI 本地引用,必须进行管理。 |
JVMTI_TYPE_JTHREAD |
110 |
Java 编程语言对象类型 - java.lang.Thread 。虚拟机 TI 类型 jthread 。返回值是 JNI 本地引用,必须进行管理。 |
JVMTI_TYPE_JCLASS |
111 |
Java 编程语言对象类型 - java.lang.Class 。 JNI 类型 jclass 。返回值是 JNI 本地引用,必须进行管理。 |
JVMTI_TYPE_JVALUE |
112 |
所有 Java 编程语言原始类型和对象类型的联合 - JNI 类型 jvalue 。表示对象类型的返回值是 JNI 本地引用,必须进行管理。 |
JVMTI_TYPE_JFIELDID |
113 |
Java 编程语言字段标识符 - JNI 类型 jfieldID 。 |
JVMTI_TYPE_JMETHODID |
114 |
Java 编程语言方法标识符 - JNI 类型 jmethodID 。 |
JVMTI_TYPE_CCHAR |
115 |
C 编程语言类型 - char 。 |
JVMTI_TYPE_CVOID |
116 |
C 编程语言类型 - void 。 |
JVMTI_TYPE_JNIENV |
117 |
JNI 环境 - JNIEnv 。应与正确的 jvmtiParamKind 一起使用以使其成为指针类型。 |
扩展函数/事件参数种类 (jvmtiParamKind
)
持续的 |
Value |
Description |
JVMTI_KIND_IN |
91 |
传入参数 - foo 。 |
JVMTI_KIND_IN_PTR |
92 |
传入指针参数 - const foo* 。 |
JVMTI_KIND_IN_BUF |
93 |
输入数组参数 - const foo* 。 |
JVMTI_KIND_ALLOC_BUF |
94 |
传出分配数组参数 - foo** 。免费使用 Deallocate 。 |
JVMTI_KIND_ALLOC_ALLOC_BUF |
95 |
分配数组参数的传出分配数组 - foo*** 。免费使用 Deallocate 。 |
JVMTI_KIND_OUT |
96 |
传出参数 - foo* 。 |
JVMTI_KIND_OUT_BUF |
97 |
传出数组参数(由代理预先分配)- foo* 。不要Deallocate 。 |
扩展函数/事件参数信息
typedef struct {
char* name;
jvmtiParamKind kind;
jvmtiParamTypes base_type;
jboolean null_ok;
} jvmtiParamInfo;
扩展功能
typedef jvmtiError (JNICALL *jvmtiExtensionFunction)
(jvmtiEnv* jvmti_env,
...);
这是特定于实现的扩展函数。
参数
Name |
Type |
Description |
jvmti_env |
jvmtiEnv * |
虚拟机 TI 环境是扩展功能的唯一固定参数。 |
... |
... |
扩展函数特定参数 |
获取扩展函数
typedef struct {
jvmtiExtensionFunction func;
char* id;
char* short_description;
jint param_count;
jvmtiParamInfo* params;
jint error_count;
jvmtiError* errors;
} jvmtiExtensionFunctionInfo;
jvmtiError
GetExtensionFunctions(jvmtiEnv* env,
jint* extension_count_ptr,
jvmtiExtensionFunctionInfo** extensions)
返回扩展函数集。
只能在 OnLoad 或 live 阶段调用
不
124
1.0
参数
Name |
Type |
Description |
extension_count_ptr |
jint* |
返回时,指向扩展函数的数量
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
extensions |
jvmtiExtensionFunctionInfo** |
返回一组扩展函数信息,每个函数一个
Agent 传递一个指向 jvmtiExtensionFunctionInfo* 的指针。返回时,jvmtiExtensionFunctionInfo* 指向新分配的大小为 *extension_count_ptr 的数组。该数组应使用 Deallocate 释放。 jvmtiExtensionFunctionInfo 的id 字段中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 jvmtiExtensionFunctionInfo 的short_description 字段中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 jvmtiExtensionFunctionInfo 的params 字段中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 jvmtiParamInfo 的name 字段中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 jvmtiExtensionFunctionInfo 的字段errors 中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 |
获取扩展事件
typedef struct {
jint extension_event_index;
char* id;
char* short_description;
jint param_count;
jvmtiParamInfo* params;
} jvmtiExtensionEventInfo;
jvmtiError
GetExtensionEvents(jvmtiEnv* env,
jint* extension_count_ptr,
jvmtiExtensionEventInfo** extensions)
返回扩展事件集。
只能在 OnLoad 或 live 阶段调用
不
125
1.0
jvmtiExtensionEventInfo
- 扩展事件信息
Field |
Type |
Description |
extension_event_index |
jint |
事件标识索引 |
id |
char* |
扩展事件的标识符,编码为 修改后的 UTF-8 字符串。使用包名称约定。例如,com.sun.hotspot.bar |
short_description |
char* |
事件的一句话描述,编码为 修改后的 UTF-8 字符串。 |
param_count |
jint |
不包括jvmtiEnv *jvmti_env 的参数个数 |
params |
jvmtiParamInfo * |
param_count 参数数组(jvmtiEnv *jvmti_env 不包括) |
参数
Name |
Type |
Description |
extension_count_ptr |
jint* |
返回时,指向扩展事件的数量
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
extensions |
jvmtiExtensionEventInfo** |
返回一组扩展事件信息,每个事件一个
Agent 传递一个指向 jvmtiExtensionEventInfo* 的指针。返回时,jvmtiExtensionEventInfo* 指向新分配的大小为 *extension_count_ptr 的数组。该数组应使用 Deallocate 释放。 jvmtiExtensionEventInfo 的id 字段中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 jvmtiExtensionEventInfo 的short_description 字段中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 jvmtiExtensionEventInfo 的params 字段中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 jvmtiParamInfo 的name 字段中返回的指针是新分配的数组。应使用 Deallocate 释放数组。 |
扩展事件
typedef void (JNICALL *jvmtiExtensionEvent)
(jvmtiEnv* jvmti_env,
...);
这是特定于实现的事件。事件处理程序设置为
SetExtensionEventCallback
。
扩展事件的事件处理程序必须声明为可变参数以匹配此定义。如果不这样做,可能会导致调用约定不匹配和某些平台上的未定义行为。
例如,如果
GetExtensionEvents
返回的
jvmtiParamInfo
表示有一个
jint
参数,则应该声明事件处理程序:
void JNICALL myHandler(jvmtiEnv* jvmti_env, ...)
注意表示可变参数的终端“
...
”。需要使用 C 编程语言的
va_*
语法提取
myHandler
中的
jint
参数。
参数
Name |
Type |
Description |
jvmti_env |
jvmtiEnv * |
虚拟机 TI 环境是扩展事件的唯一固定参数。 |
... |
... |
扩展事件特定参数 |
设置扩展事件回调
jvmtiError
SetExtensionEventCallback(jvmtiEnv* env,
jint extension_event_index,
jvmtiExtensionEvent callback)
设置扩展事件的回调函数并启用该事件。或者,如果回调是 NULL
,则禁用该事件。请注意,与标准事件不同,设置回调和启用事件是一个单一的操作。
只能在 OnLoad 或 live 阶段调用
不
126
1.0
能力
能力函数:
能力类型:
功能函数允许您更改 JVM 可用的功能 TI--即JVM 可以调用TI函数,可以产生什么事件,这些事件和函数可以提供什么功能。
每个函数和事件的“能力”部分描述了它们关联的能力(如果有的话)。 “必需的功能”意味着它可供使用,无需添加任何功能即可使用它。 “可选功能”是指代理必须具备该能力才能使用。要拥有能力,代理必须添加功能。 “可选特性”描述了如果添加,扩展特性集的能力。
每个 JVM 的潜在可用功能 TI 实现不同。根据实现,能力:
- 可能永远不会被添加
- 可以在任何环境中的
OnLoad
或实时阶段添加
- 只能在
OnLoad
阶段添加
- 一次只能被一个环境所拥有
- 一次只能被一个环境所拥有,并且只能在
OnLoad
阶段
- 等等 ...
通常,添加功能可能会导致执行速度、启动时间和/或内存占用方面的成本。请注意,使用能力的开销与拥有能力的开销完全不同。以单步执行为例。当启用单步执行时(即,当启用事件并因此主动发送事件时)在任何实现中,在每条指令上发送和处理事件的开销都是巨大的。然而,拥有能力的开销可能很小或很大,这取决于实现。此外,功能何时以及是否可能可用取决于实现。一些示例:
- 一个 VM 可能通过将字节码编译成本机代码来执行所有执行,并且无法生成单步指令。在此实现中,无法添加功能。
- 另一个 VM 可以随时将执行切换到单步执行解释器。在此实现中,拥有该功能没有开销,可以随时添加。
- 另一个 VM 可能能够在启动时选择字节码编译或支持单步执行的解释执行引擎,但无法在它们之间切换。在此实现中,需要在
OnLoad
阶段(字节码执行开始之前)添加该功能,即使从未使用单步执行,也会对执行速度产生很大影响。
- 还有另一个 VM 可能能够将“正在单步执行”检查添加到已编译的字节码或生成的解释器中。再次在此实现中,需要在
OnLoad
阶段添加功能,但开销(每条指令的测试和分支)将大大减少。
每个 JVM TI environment 拥有自己的一套功能。最初,该集合是空的。必须添加任何所需的功能。如果可能,应在 OnLoad
阶段添加功能。对于大多数虚拟机,某些功能需要为虚拟机进行特殊设置,并且此设置必须在虚拟机开始执行之前的 OnLoad
阶段进行。添加功能后,只有在环境明确放弃的情况下才能将其删除。
代理可以 确定此 VM 可能提供的功能 、 添加要使用的功能 、 释放不再需要的能力 和 检查当前可用的功能 。
能力示例
例如,刚启动的代理(在 OnLoad
函数中)想要启用所有可能的功能。请注意,一般来说,这是不可取的,因为代理可能会因未使用的功能而遭受性能损失。在 C 中,代码可能如下所示:
jvmtiCapabilities capa;
jvmtiError err;
err = (*jvmti)->GetPotentialCapabilities(jvmti, &capa);
if (err == JVMTI_ERROR_NONE) {
err = (*jvmti)->AddCapabilities(jvmti, &capa);
例如,如果一个代理想要检查它是否可以获取某个方法的字节码(也就是说,它想要检查它之前是否添加了此功能并且没有放弃它),代码在 C 中可能如下所示:
jvmtiCapabilities capa;
jvmtiError err;
err = (*jvmti)->GetCapabilities(jvmti, &capa);
if (err == JVMTI_ERROR_NONE) {
if (capa.can_get_bytecodes) { ... } }
能力结构
此类别中的函数使用此功能结构,其中包含与每个功能相对应的布尔标志:
typedef struct {
unsigned int can_tag_objects : 1;
unsigned int can_generate_field_modification_events : 1;
unsigned int can_generate_field_access_events : 1;
unsigned int can_get_bytecodes : 1;
unsigned int can_get_synthetic_attribute : 1;
unsigned int can_get_owned_monitor_info : 1;
unsigned int can_get_current_contended_monitor : 1;
unsigned int can_get_monitor_info : 1;
unsigned int can_pop_frame : 1;
unsigned int can_redefine_classes : 1;
unsigned int can_signal_thread : 1;
unsigned int can_get_source_file_name : 1;
unsigned int can_get_line_numbers : 1;
unsigned int can_get_source_debug_extension : 1;
unsigned int can_access_local_variables : 1;
unsigned int can_maintain_original_method_order : 1;
unsigned int can_generate_single_step_events : 1;
unsigned int can_generate_exception_events : 1;
unsigned int can_generate_frame_pop_events : 1;
unsigned int can_generate_breakpoint_events : 1;
unsigned int can_suspend : 1;
unsigned int can_redefine_any_class : 1;
unsigned int can_get_current_thread_cpu_time : 1;
unsigned int can_get_thread_cpu_time : 1;
unsigned int can_generate_method_entry_events : 1;
unsigned int can_generate_method_exit_events : 1;
unsigned int can_generate_all_class_hook_events : 1;
unsigned int can_generate_compiled_method_load_events : 1;
unsigned int can_generate_monitor_events : 1;
unsigned int can_generate_vm_object_alloc_events : 1;
unsigned int can_generate_native_method_bind_events : 1;
unsigned int can_generate_garbage_collection_events : 1;
unsigned int can_generate_object_free_events : 1;
unsigned int can_force_early_return : 1;
unsigned int can_get_owned_monitor_stack_depth_info : 1;
unsigned int can_get_constant_pool : 1;
unsigned int can_set_native_method_prefix : 1;
unsigned int can_retransform_classes : 1;
unsigned int can_retransform_any_class : 1;
unsigned int can_generate_resource_exhaustion_heap_events : 1;
unsigned int can_generate_resource_exhaustion_threads_events : 1;
unsigned int can_generate_early_vmstart : 1;
unsigned int can_generate_early_class_hook_events : 1;
unsigned int can_generate_sampled_object_alloc_events : 1;
unsigned int can_support_virtual_threads : 1;
unsigned int : 3;
unsigned int : 16;
unsigned int : 16;
unsigned int : 16;
unsigned int : 16;
unsigned int : 16;
} jvmtiCapabilities;
获得潜在能力
jvmtiError
GetPotentialCapabilities(jvmtiEnv* env,
jvmtiCapabilities* capabilities_ptr)
通过 capabilities_ptr
JVM 返回 此时此环境可能拥有的 TI 功能。返回的能力与 VM 实现的完整能力集在两种情况下不同:另一个环境拥有只能由一个环境拥有的能力,或者当前阶段是 live,某些能力只能在OnLoad
阶段添加。 AddCapabilities
函数可用于设置任何或所有或这些功能。当前拥有的能力包括在内。
通常此函数用于 OnLoad
函数中。某些虚拟机可能允许在运行阶段添加一组有限的功能。在这种情况下,潜在可用功能集可能与 OnLoad
阶段集不同。
请参阅 能力示例。
只能在 OnLoad 或 live 阶段调用
不
140
1.0
参数
Name |
Type |
Description |
capabilities_ptr |
jvmtiCapabilities* |
返回时,指向 JVM 可能添加的 TI 功能。
Agent 传递一个指向 jvmtiCapabilities 的指针。返回时,jvmtiCapabilities 已设置。 |
添加功能
jvmtiError
AddCapabilities(jvmtiEnv* env,
const jvmtiCapabilities* capabilities_ptr)
通过在 *
capabilities_ptr
中添加值设置为 1 (1
) 的功能来设置新功能。保留所有以前的功能。通常此函数用于 OnLoad
函数中。某些虚拟机可能允许在运行阶段添加一组有限的功能。
请参阅 能力示例。
只能在 OnLoad 或 live 阶段调用
不
142
1.0
参数
Name |
Type |
Description |
capabilities_ptr |
const jvmtiCapabilities* |
指向 JVM 要添加的 TI 功能。
Agent 传入一个指向 jvmtiCapabilities 的指针。 |
放弃能力
jvmtiError
RelinquishCapabilities(jvmtiEnv* env,
const jvmtiCapabilities* capabilities_ptr)
放弃 *
capabilities_ptr
中值设置为一 (1
) 的能力。一些实现可能只允许一个环境具有一种能力(参见 能力介绍 )。此功能释放功能,以便其他代理可以使用它们。保留所有其他功能。该功能将不再出现在 GetCapabilities
中。试图放弃代理不具备的能力不是错误。
只能在 OnLoad 或 live 阶段调用
不
143
1.0
参数
Name |
Type |
Description |
capabilities_ptr |
const jvmtiCapabilities* |
指向 JVM TI 能力放弃。
Agent 传入一个指向 jvmtiCapabilities 的指针。 |
获得能力
jvmtiError
GetCapabilities(jvmtiEnv* env,
jvmtiCapabilities* capabilities_ptr)
通过 capabilities_ptr
返回可选的 JVM 此环境当前拥有的 TI 功能。每个拥有的能力在 能力结构 的相应字段中由一个 (1
) 表示。除非已使用 AddCapabilities
成功添加,否则环境不具备能力。环境只有在使用 RelinquishCapabilities
放弃时才会失去对能力的拥有。因此,此函数返回已进行的 AddCapabilities
和 RelinquishCapabilities
调用的最终结果。
请参阅 能力示例。
参数
Name |
Type |
Description |
capabilities_ptr |
jvmtiCapabilities* |
返回时,指向 JVM TI 能力。
Agent 传递一个指向 jvmtiCapabilities 的指针。返回时,jvmtiCapabilities 已设置。 |
计时器
定时器功能:
定时器类型:
这些函数提供计时信息。未指定更新时间的分辨率。它们提供纳秒级精度,但不一定是纳秒级精度。可以使用定时器信息函数访问有关定时器的详细信息,例如它们的最大值。
定时器信息
每个定时器的信息函数返回这个数据结构。
typedef struct {
jlong max_value;
jboolean may_skip_forward;
jboolean may_skip_backward;
jvmtiTimerKind kind;
jlong reserved1;
jlong reserved2;
} jvmtiTimerInfo;
jvmtiTimerInfo
- 计时器信息
Field |
Type |
Description |
max_value |
jlong |
计时器可以达到的最大值。达到此值后,计时器返回到零。这是一个无符号值。如果作为 jlong(有符号值)进行测试或打印,它可能看起来是一个负数。 |
may_skip_forward |
jboolean |
如果为 true,则可以从外部调整计时器并因此向前跳转。如果为 false,定时器值将永远不会比实时增加得更快。 |
may_skip_backward |
jboolean |
如果为 true,则可以从外部调整计时器,从而向后跳。如果为假,定时器值将单调递增。 |
kind |
jvmtiTimerKind |
定时器的那种。在不区分用户时间和系统时间的平台上,返回JVMTI_TIMER_TOTAL_CPU 。 |
reserved1 |
jlong |
保留以供将来使用。 |
reserved2 |
jlong |
保留以供将来使用。 |
定时器种类在哪里——
计时器种类 (jvmtiTimerKind
)
持续的 |
Value |
Description |
JVMTI_TIMER_USER_CPU |
30 |
线程处于用户模式的 CPU 时间。 |
JVMTI_TIMER_TOTAL_CPU |
31 |
线程处于用户或系统模式的 CPU 时间。 |
JVMTI_TIMER_ELAPSED |
32 |
经过的时间。 |
获取当前线程 CPU 计时器信息
jvmtiError
GetCurrentThreadCpuTimerInfo(jvmtiEnv* env,
jvmtiTimerInfo* info_ptr)
获取有关 GetCurrentThreadCpuTime
计时器的信息。 jvmtiTimerInfo
结构的字段填充了有关计时器的详细信息。此信息特定于平台和 GetCurrentThreadCpuTime
的实现,因此不会因线程而异,也不会在 VM 的特定调用期间发生变化。
请注意,GetCurrentThreadCpuTime
和 GetThreadCpuTime
的实现可能不同,因此 GetCurrentThreadCpuTimerInfo
和 GetThreadCpuTimerInfo
返回的值可能不同——有关更多信息,请参阅 GetCurrentThreadCpuTime
。
获取当前线程 CPU 时间
jvmtiError
GetCurrentThreadCpuTime(jvmtiEnv* env,
jlong* nanos_ptr)
返回当前线程使用的 CPU 时间。
请注意,GetThreadCpuTime
函数为任何线程(包括当前线程)提供 CPU 时间。 GetCurrentThreadCpuTime
的存在是为了支持无法为当前线程以外的线程提供 CPU 时间的平台,或者为当前线程提供更准确信息的平台(参见 GetCurrentThreadCpuTimerInfo
与 GetThreadCpuTimerInfo
)。当前线程可能不是虚拟线程。否则,将返回错误代码JVMTI_ERROR_UNSUPPORTED_OPERATION
。在许多平台上,此调用将等效于:
GetThreadCpuTime(env, NULL, nanos_ptr)
参数
Name |
Type |
Description |
nanos_ptr |
jlong* |
返回时,指向此线程使用的 CPU 时间(以纳秒为单位)。这是一个无符号值。如果作为 jlong(有符号值)进行测试或打印,它可能看起来是一个负数。
Agent 传递一个指向 jlong 的指针。返回时,jlong 已设置。 |
获取线程 CPU 定时器信息
jvmtiError
GetThreadCpuTimerInfo(jvmtiEnv* env,
jvmtiTimerInfo* info_ptr)
获取有关 GetThreadCpuTime
计时器的信息。 jvmtiTimerInfo
结构的字段填充了有关计时器的详细信息。此信息特定于平台和 GetThreadCpuTime
的实现,因此不会因线程而异,也不会在 VM 的特定调用期间发生变化。
请注意,GetCurrentThreadCpuTime
和 GetThreadCpuTime
的实现可能不同,因此 GetCurrentThreadCpuTimerInfo
和 GetThreadCpuTimerInfo
返回的值可能不同——有关更多信息,请参阅 GetCurrentThreadCpuTime
。
获取线程 CPU 时间
jvmtiError
GetThreadCpuTime(jvmtiEnv* env,
jthread thread,
jlong* nanos_ptr)
返回指定线程使用的 CPU 时间。
使用 GetThreadCpuTimerInfo
获取有关此计时器的信息。
参数
Name |
Type |
Description |
thread |
jthread |
要查询的线程。 thread 可能不是虚拟线程。否则,将返回错误代码JVMTI_ERROR_UNSUPPORTED_OPERATION 。如果 thread 是 NULL ,则使用当前线程。 |
nanos_ptr |
jlong* |
返回时,指向指定线程使用的 CPU 时间(以纳秒为单位)。这是一个无符号值。如果作为 jlong(有符号值)进行测试或打印,它可能看起来是一个负数。
Agent 传递一个指向 jlong 的指针。返回时,jlong 已设置。 |
获取定时器信息
jvmtiError
GetTimerInfo(jvmtiEnv* env,
jvmtiTimerInfo* info_ptr)
获取有关 GetTime
计时器的信息。 jvmtiTimerInfo
结构的字段填充了有关计时器的详细信息。在 VM 的特定调用期间,此信息不会更改。
参数
Name |
Type |
Description |
info_ptr |
jvmtiTimerInfo* |
返回时,填充了描述 GetTime 返回时间的信息。
Agent 传递一个指向 jvmtiTimerInfo 的指针。返回时,jvmtiTimerInfo 已设置。 |
获取时间
jvmtiError
GetTime(jvmtiEnv* env,
jlong* nanos_ptr)
返回系统计时器的当前值,以纳秒为单位。
返回的值表示自某个固定但任意时间以来的纳秒数(可能在未来,因此值可能为负)。此函数提供纳秒级精度,但不一定是纳秒级精度。不保证值更改的频率。
使用 GetTimerInfo
获取有关此计时器的信息。
参数
Name |
Type |
Description |
nanos_ptr |
jlong* |
返回时,指向以纳秒为单位的时间。这是一个无符号值。如果作为 jlong(有符号值)进行测试或打印,它可能看起来是一个负数。
Agent 传递一个指向 jlong 的指针。返回时,jlong 已设置。 |
获取可用的处理器
jvmtiError
GetAvailableProcessors(jvmtiEnv* env,
jint* processor_count_ptr)
返回 Java 虚拟机可用的处理器数量。
该值可能会在虚拟机的特定调用期间发生变化。因此,对可用处理器数量敏感的应用程序应偶尔轮询此属性。
参数
Name |
Type |
Description |
processor_count_ptr |
jint* |
返回时,指向虚拟机可用的最大处理器数;永远不会小于一个。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
类加载器搜索
类加载器搜索功能:
这些函数允许代理添加到类加载器搜索类的位置。这对于在正确的类加载器下安装工具很有用。
添加到 Bootstrap 类加载器搜索
jvmtiError
AddToBootstrapClassLoaderSearch(jvmtiEnv* env,
const char* segment)
此函数可用于使检测类由引导类加载器定义。看Java™ 虚拟机规范,第 5.3.1 章.引导类加载器搜索类失败后,也会搜索指定的平台相关搜索路径segment
。 segment
中只能指定一个段。可以多次调用此函数来添加多个段,将按照调用此函数的顺序搜索段。
在 OnLoad
阶段,该函数可用于指定在引导类加载器搜索类失败后要搜索的任何依赖于平台的搜索路径段。该段通常是一个目录或 JAR 文件。
在实时阶段,segment
可用于指定任何依赖于平台的路径到 文件。代理应该注意 JAR 文件不包含任何类或资源,除了那些由引导类加载器为检测目的定义的类或资源。
Java™ 虚拟机规范指定后续尝试解析 Java 虚拟机先前未成功尝试解析的符号引用总是失败,并抛出与初始解析尝试相同的错误。因此,如果 JAR 文件包含对应于 Java 虚拟机未能成功解析引用的类的条目,则后续解析该引用的尝试将失败,并出现与初始尝试相同的错误。
只能在 OnLoad 或 live 阶段调用
不
149
1.0
参数
Name |
Type |
Description |
segment |
const char* |
平台相关的搜索路径段,编码为 修改后的 UTF-8 字符串。
代理传入一个 char 数组。 |
添加到系统类加载器搜索
jvmtiError
AddToSystemClassLoaderSearch(jvmtiEnv* env,
const char* segment)
此函数可用于使检测类由系统类加载器定义。看Java™ 虚拟机规范,第 5.3.2 章.类加载器搜索类失败后,也会搜索指定的平台相关搜索路径segment
。 segment
中只能指定一个段。可以多次调用此函数来添加多个段,将按照调用此函数的顺序搜索段。
在 OnLoad
阶段,该函数可用于指定在系统类加载器搜索类失败后要搜索的任何依赖于平台的搜索路径段。该段通常是一个目录或 JAR 文件。
在实时阶段,segment
是一个依赖于平台的路径,指向 文件,在系统类加载器搜索类失败后将被搜索。代理应该注意 JAR 文件不包含任何类或资源,除了那些由系统类加载器为检测目的定义的类或资源。
在实时阶段,如果系统类加载器实现了一个方法名称 appendToClassPathForInstrumentation
,它接受一个类型为 java.lang.String
的参数,则系统类加载器支持添加要搜索的 JAR 文件。该方法不需要具有 public
访问权限。
Java™ 虚拟机规范指定后续尝试解析 Java 虚拟机先前未成功尝试解析的符号引用总是失败,并抛出与初始解析尝试相同的错误。因此,如果 JAR 文件包含对应于 Java 虚拟机未能成功解析引用的类的条目,则后续解析该引用的尝试将失败,并出现与初始尝试相同的错误。
只能在 OnLoad 或 live 阶段调用
不
151
1.1
参数
Name |
Type |
Description |
segment |
const char* |
平台相关的搜索路径段,编码为 修改后的 UTF-8 字符串。
代理传入一个 char 数组。 |
系统属性
系统属性函数:
这些函数获取和设置系统属性。
获取系统属性
jvmtiError
GetSystemProperties(jvmtiEnv* env,
jint* count_ptr,
char*** property_ptr)
返回可与 GetSystemProperty
一起使用的 VM 系统属性键列表。强烈建议虚拟机提供以下属性键:
java.vm.vendor
java.vm.version
java.vm.name
java.vm.info
java.library.path
java.class.path
提供对 VM 定义和使用的系统属性的访问。包括在命令行上设置的属性。这允许在 VM 甚至开始执行字节码之前获取和设置这些属性。由于这是系统属性的 VM 视图,可用属性集通常与 java.lang.System.getProperties
中的不同。 JNI 方法调用可用于访问 java.lang.System.getProperties
。
属性集可能会在执行期间增长。
只能在 OnLoad 或 live 阶段调用
不
130
1.0
参数
Name |
Type |
Description |
count_ptr |
jint* |
返回时,指向返回的属性键的数量。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
property_ptr |
char*** |
返回时,指向属性键数组,编码为 修改后的 UTF-8 字符串。
Agent 传递一个指向 char** 的指针。返回时,char** 指向新分配的大小为 *count_ptr 的数组,其中的每个元素也是新分配的。该数组应使用 Deallocate 释放。每个元素都应该用 Deallocate 释放。 |
获取系统属性
jvmtiError
GetSystemProperty(jvmtiEnv* env,
const char* property,
char** value_ptr)
返回给定属性键的 VM 系统属性值。
函数 GetSystemProperties
返回可以使用的属性键集。可以检索的属性可能会在执行期间增长。
由于这是系统属性的 VM 视图,因此属性值可能与 java.lang.System.getProperty(String)
返回的值不同。典型的 VM 可能会在该类的初始化期间将 VM 系统属性的值复制到 java.lang.System
持有的 Properties
中。此后,对 VM 系统属性(使用 SetSystemProperty
)或 java.lang.System
系统属性(使用 java.lang.System.setProperty(String,String)
)的任何更改都将导致值出现差异。 JNI 方法调用可用于访问 java.lang.System.getProperty(String)
。
只能在 OnLoad 或 live 阶段调用
不
131
1.0
参数
Name |
Type |
Description |
property |
const char* |
要检索的属性的键,编码为 修改后的 UTF-8 字符串。
代理传入一个 char 数组。 |
value_ptr |
char** |
返回时,指向属性值,编码为 修改后的 UTF-8 字符串。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。 |
设置系统属性
jvmtiError
SetSystemProperty(jvmtiEnv* env,
const char* property,
const char* value_ptr)
设置 VM 系统属性值。
GetSystemProperties
函数返回一组属性键,其中一些可能是可设置的。参见 GetSystemProperty
。
只能在 OnLoad 阶段调用
不
132
1.0
一般的
一般功能:
一般类型:
通用标志和常量:
获取阶段
typedef enum {
JVMTI_PHASE_ONLOAD = 1,
JVMTI_PHASE_PRIMORDIAL = 2,
JVMTI_PHASE_START = 6,
JVMTI_PHASE_LIVE = 4,
JVMTI_PHASE_DEAD = 8
} jvmtiPhase;
jvmtiError
GetPhase(jvmtiEnv* env,
jvmtiPhase* phase_ptr)
返回 VM 执行的当前阶段。这些阶段按顺序进行:
执行阶段 (jvmtiPhase
)
持续的 |
Value |
Description |
JVMTI_PHASE_ONLOAD |
1 |
OnLoad 阶段:在 Agent_OnLoad 中,或者对于静态链接代理,在 Agent_OnLoad_<agent-lib-name> 函数中。 |
JVMTI_PHASE_PRIMORDIAL |
2 |
原始阶段:从 Agent_OnLoad 或 Agent_OnLoad_<agent-lib-name> 返回和 VMStart 事件之间。 |
JVMTI_PHASE_START |
6 |
开始阶段:发送VMStart 事件时,直到发送VMInit 事件。 |
JVMTI_PHASE_LIVE |
4 |
实时阶段:发送VMInit 事件时,直到VMDeath 事件返回。 |
JVMTI_PHASE_DEAD |
8 |
停滞阶段:VMDeath 事件返回后或启动失败后。 |
在启动失败的情况下,VM 将跳过中间阶段直接进入死阶段,并且不会发送 VMInit
和 VMDeath
事件。
大多数 JVM TI 功能仅在实时阶段运行。以下函数在 OnLoad
或 live 阶段运行:
以下功能仅在OnLoad
阶段运行:
以下功能在启动或实时阶段运行:
以下功能在任何阶段运行:
JNI 函数(Invocation API 除外)只能在启动或运行阶段使用。
大多数 JVM TI 事件仅在实时阶段发送。以下事件在其他阶段运行:
参数
Name |
Type |
Description |
phase_ptr |
jvmtiPhase* |
返回时,指向相位。
Agent 传递一个指向 jvmtiPhase 的指针。返回时,jvmtiPhase 已设置。 |
处理环境
jvmtiError
DisposeEnvironment(jvmtiEnv* env)
关闭 JVM 使用 JNI GetEnv
创建的 TI 连接(参见 JVM TI Environments )。处理环境持有的任何资源。此调用不会恢复被此环境挂起的线程,这必须由代理显式完成。此环境通过调用 JVM 分配的内存 TI 功能未发布,这可以由代理通过调用 Deallocate
明确完成。此环境创建的原始监视器不会被销毁,这可以由代理通过调用 DestroyRawMonitor
显式完成。在此环境创建的原始监视器上等待的线程状态不受影响。
此环境的任何 本机方法前缀 都将被取消设置;在调用 dispose 之前,代理必须删除任何带前缀的本机方法。
此环境持有的任何 capabilities 都将被放弃。
此环境启用的事件将不再发送,但当前正在运行的事件处理程序将继续运行。在设计事件处理程序时必须小心谨慎,事件处理程序的环境可能会被释放,从而在执行期间变得无效。
此调用后可能无法使用此环境。此调用返回给调用者。
设置环境本地存储
jvmtiError
SetEnvironmentLocalStorage(jvmtiEnv* env,
const void* data)
VM 存储与每个环境关联的指针值。这个指针值被称为环境本地存储.这个值是NULL
除非用这个函数设置。代理可以分配存储环境特定信息的内存。通过设置环境本地存储,然后可以使用 GetEnvironmentLocalStorage
访问它。
由代理调用以设置 JVM 的值 TI环境-本地存储。虚拟机 TI 向代理提供指针大小的环境本地存储,可用于记录每个环境的信息。
参数
Name |
Type |
Description |
data |
const void * |
要输入环境本地存储的值。
Agent 传入一个指针。如果 data 为 NULL ,则值设置为 NULL 。 |
获取环境本地存储
jvmtiError
GetEnvironmentLocalStorage(jvmtiEnv* env,
void** data_ptr)
被agent调用获取JVM的值 TI环境-本地存储。
获取版本号
jvmtiError
GetVersionNumber(jvmtiEnv* env,
jint* version_ptr)
返回 JVM TI 版本来自 version_ptr
。返回值是版本标识符。版本标识符包括主要版本、次要版本和微版本以及接口类型。
版本接口类型
持续的 |
Value |
Description |
JVMTI_VERSION_INTERFACE_JNI |
0x00000000 |
JNI 的 JVMTI_VERSION_MASK_INTERFACE_TYPE 值。 |
JVMTI_VERSION_INTERFACE_JVMTI |
0x30000000 |
JVM 的 JVMTI_VERSION_MASK_INTERFACE_TYPE 值 TI. |
版本掩码
持续的 |
Value |
Description |
JVMTI_VERSION_MASK_INTERFACE_TYPE |
0x70000000 |
用于提取接口类型的掩码。由JVMTI_VERSION_MASK_INTERFACE_TYPE 屏蔽的此函数返回的版本值始终为JVMTI_VERSION_INTERFACE_JVMTI ,因为这是一个 JVM TI 功能。 |
JVMTI_VERSION_MASK_MAJOR |
0x0FFF0000 |
用于提取主版本号的掩码。 |
JVMTI_VERSION_MASK_MINOR |
0x0000FF00 |
用于提取次要版本号的掩码。 |
JVMTI_VERSION_MASK_MICRO |
0x000000FF |
用于提取微版本号的掩码。 |
版本转换
持续的 |
Value |
Description |
JVMTI_VERSION_SHIFT_MAJOR |
16 |
Shift 以提取主要版本号。 |
JVMTI_VERSION_SHIFT_MINOR |
8 |
Shift 以提取次要版本号。 |
JVMTI_VERSION_SHIFT_MICRO |
0 |
Shift 提取微版本号。 |
参数
Name |
Type |
Description |
version_ptr |
jint* |
返回时,指向 JVM TI 版本。
Agent 传递一个指向 jint 的指针。返回时,jint 已设置。 |
获取错误名称
jvmtiError
GetErrorName(jvmtiEnv* env,
jvmtiError error,
char** name_ptr)
返回 错误代码 的符号名称。
例如,GetErrorName(env, JVMTI_ERROR_NONE, &err_name)
将在 err_name
中返回字符串 "JVMTI_ERROR_NONE"
。
参数
Name |
Type |
Description |
error |
jvmtiError |
错误代码。 |
name_ptr |
char** |
返回时,指向错误名称。该名称被编码为 修改后的 UTF-8 字符串,但仅限于 ASCII 子集。
Agent 传递一个指向 char* 的指针。返回时,char* 指向一个新分配的数组。该数组应使用 Deallocate 释放。 |
设置详细标志
typedef enum {
JVMTI_VERBOSE_OTHER = 0,
JVMTI_VERBOSE_GC = 1,
JVMTI_VERBOSE_CLASS = 2,
JVMTI_VERBOSE_JNI = 4
} jvmtiVerboseFlag;
jvmtiError
SetVerboseFlag(jvmtiEnv* env,
jvmtiVerboseFlag flag,
jboolean value)
详细标志枚举 (jvmtiVerboseFlag
)
持续的 |
Value |
Description |
JVMTI_VERBOSE_OTHER |
0 |
除以下内容外的详细输出。 |
JVMTI_VERBOSE_GC |
1 |
详细的垃圾收集器输出,就像用 -verbose:gc 指定的那样。 |
JVMTI_VERBOSE_CLASS |
2 |
详细的类加载输出,就像用 -verbose:class 指定的那样。 |
JVMTI_VERBOSE_JNI |
4 |
详细的 JNI 输出,就像用 -verbose:jni 指定的那样。 |
控制详细输出。这是通常发送到 stderr
的输出。
获取 JLocation 格式
typedef enum {
JVMTI_JLOCATION_JVMBCI = 1,
JVMTI_JLOCATION_MACHINEPC = 2,
JVMTI_JLOCATION_OTHER = 0
} jvmtiJlocationFormat;
jvmtiError
GetJLocationFormat(jvmtiEnv* env,
jvmtiJlocationFormat* format_ptr)
尽管最大的功能是通过引用虚拟机字节码索引的位置信息实现的,但 jlocation
的定义有意不受约束,以允许没有此信息的 VM 实现。
此函数描述了此 VM 中使用的 jlocation
的表示。如果返回的格式为 JVMTI_JLOCATION_JVMBCI
,则 jlocation
可以用作 GetBytecodes
返回的数组的索引。
参数
Name |
Type |
Description |
format_ptr |
jvmtiJlocationFormat* |
返回时,指向 jlocation 值的格式标识符。
Agent 传递一个指向 jvmtiJlocationFormat 的指针。返回时,jvmtiJlocationFormat 已设置。 |
堆监控
堆监控功能:
设置堆采样间隔
jvmtiError
SetHeapSamplingInterval(jvmtiEnv* env,
jint sampling_interval)
分配对象时生成 SampledObjectAlloc
事件。每个线程都保留一个已分配字节数的计数器。仅当该计数器自上次采样以来超过 sampling_interval
的平均值时才会生成该事件。
将 sampling_interval
设置为 0 将导致在考虑新间隔后,系统支持的每个分配都会生成一个事件。
请注意,更新新的采样间隔可能需要不同数量的分配来激发内部数据结构更新。因此,将采样间隔视为平均值很重要。这包括间隔 0,其中可能不会为每个分配立即生成事件。
只能在 OnLoad 或 live 阶段调用
不
156
11
参数
Name |
Type |
Description |
sampling_interval |
jint |
以字节为单位的采样间隔。采样器使用统计方法生成一个事件,平均而言,给定线程每分配 sampling_interval 字节内存一次。
一旦考虑了新的采样间隔,0 作为采样间隔将为每个分配生成一个样本。
注意:此功能的开销与采样间隔直接相关。较高的采样间隔(例如 1024 字节)会产生较高的开销。较小的间隔(例如 1024KB)的开销会低得多。采样仅应在了解它可能会影响性能的情况下使用。 |
错误
每个 JVM TI 函数返回一个jvmtiError
错误代码。
agent负责调用JVM TI 函数具有有效参数并在适当的上下文中(附加调用线程、相位正确等)。检测某些错误条件对于实现来说可能是困难的、低效的或不可能的。 功能特定的必要错误 中列出的错误必须由实现检测到。所有其他错误表示对错误情况的建议响应。
普遍错误
任何函数都可能返回以下错误
-
JVMTI_ERROR_NONE (0)
-
没有发生错误。这是函数成功完成时返回的错误代码。
-
JVMTI_ERROR_NULL_POINTER (100)
-
指针意外地是
NULL
。
-
JVMTI_ERROR_OUT_OF_MEMORY (110)
-
该函数试图分配内存,但没有更多内存可用于分配。
-
JVMTI_ERROR_ACCESS_DENIED (111)
-
此虚拟机中尚未启用所需的功能。
-
JVMTI_ERROR_UNATTACHED_THREAD (115)
-
用于调用此函数的线程未附加到虚拟机。必须从附加线程进行调用。请参阅 JNI 调用 API 中的
AttachCurrentThread
。
-
JVMTI_ERROR_INVALID_ENVIRONMENT (116)
-
虚拟机 提供的 TI 环境不再连接或不是环境。
-
JVMTI_ERROR_WRONG_PHASE (112)
-
所需的功能在当前的 阶段 中不可用。如果虚拟机已完成运行,则始终返回。
-
JVMTI_ERROR_INTERNAL (113)
-
发生意外的内部错误。
功能特定的必要错误
某些 JVM 返回以下错误 TI 函数并且必须在条件发生时由实现返回。
-
JVMTI_ERROR_INVALID_PRIORITY (12)
-
优先级无效。
-
JVMTI_ERROR_THREAD_NOT_SUSPENDED (13)
-
线程未暂停。
-
JVMTI_ERROR_THREAD_SUSPENDED (14)
-
线程已暂停。
-
JVMTI_ERROR_THREAD_NOT_ALIVE (15)
-
此操作要求线程处于活动状态——也就是说,它必须已启动且尚未终止。
-
JVMTI_ERROR_CLASS_NOT_PREPARED (22)
-
该类已加载但尚未准备好。
-
JVMTI_ERROR_NO_MORE_FRAMES (31)
-
在指定深度没有 Java 编程语言或 JNI 堆栈帧。
-
JVMTI_ERROR_OPAQUE_FRAME (32)
-
有关框架的信息不可用(例如,对于本机框架)。
-
JVMTI_ERROR_DUPLICATE (40)
-
项目已经设置。
-
JVMTI_ERROR_NOT_FOUND (41)
-
未找到所需的元素(例如字段或断点)
-
JVMTI_ERROR_NOT_MONITOR_OWNER (51)
-
该线程不拥有原始监视器。
-
JVMTI_ERROR_INTERRUPT (52)
-
呼叫在完成之前被中断。
-
JVMTI_ERROR_UNMODIFIABLE_CLASS (79)
-
类不能修改。
-
JVMTI_ERROR_UNMODIFIABLE_MODULE (80)
-
无法修改模块。
-
JVMTI_ERROR_NOT_AVAILABLE (98)
-
该功能在此虚拟机中不可用。
-
JVMTI_ERROR_ABSENT_INFORMATION (101)
-
请求的信息不可用。
-
JVMTI_ERROR_INVALID_EVENT_TYPE (102)
-
无法识别指定的事件类型 ID。
-
JVMTI_ERROR_NATIVE_METHOD (104)
-
请求的信息不适用于本机方法。
-
JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED (106)
-
类加载器不支持此操作。
功能特定代理错误
某些 JVM 返回以下错误 德州仪器功能。如果代理传递的参数无效或在无效上下文中使用,则返回它们。不需要实现来检测这些错误。
-
JVMTI_ERROR_INVALID_THREAD (10)
-
传递的线程不是有效线程。
-
JVMTI_ERROR_INVALID_FIELDID (25)
-
无效字段。
-
JVMTI_ERROR_INVALID_MODULE (26)
-
模块无效。
-
JVMTI_ERROR_INVALID_METHODID (23)
-
无效的方法。
-
JVMTI_ERROR_INVALID_LOCATION (24)
-
位置无效。
-
JVMTI_ERROR_INVALID_OBJECT (20)
-
无效的对象。
-
JVMTI_ERROR_INVALID_CLASS (21)
-
类无效。
-
JVMTI_ERROR_TYPE_MISMATCH (34)
-
该变量不是所用函数的合适类型。
-
JVMTI_ERROR_INVALID_SLOT (35)
-
插槽无效。
-
JVMTI_ERROR_MUST_POSSESS_CAPABILITY (99)
-
在此环境中使用的功能是错误的。
-
JVMTI_ERROR_INVALID_THREAD_GROUP (11)
-
线程组无效。
-
JVMTI_ERROR_INVALID_MONITOR (50)
-
原始监视器无效。
-
JVMTI_ERROR_ILLEGAL_ARGUMENT (103)
-
非法参数。
-
JVMTI_ERROR_INVALID_TYPESTATE (65)
-
线程的状态已被修改,现在不一致。
-
JVMTI_ERROR_UNSUPPORTED_VERSION (68)
-
此 VM 不支持新类文件的版本号。
-
JVMTI_ERROR_INVALID_CLASS_FORMAT (60)
-
新类文件格式错误(VM 将返回
ClassFormatError
)。
-
JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION (61)
-
新的类文件定义将导致循环定义(VM 将返回
ClassCircularityError
)。
-
JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED (63)
-
一个新的类文件需要添加一个方法。
-
JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED (64)
-
新的类版本更改了一个字段。
-
JVMTI_ERROR_FAILS_VERIFICATION (62)
-
类字节验证失败。
-
JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED (66)
-
新类版本的直接超类不同,或者直接实现的接口集不同。
-
JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED (67)
-
新类版本不声明在旧类版本中声明的方法。
-
JVMTI_ERROR_NAMES_DONT_MATCH (69)
-
新类文件中定义的类名与旧类对象中的名称不同。
-
JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED (70)
-
新的类版本具有不同的修饰符。
-
JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED (71)
-
新类版本中的方法与旧类版本中的对应方法具有不同的修饰符。
-
JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED (72)
-
新类版本在类属性方面存在不受支持的差异。
-
JVMTI_ERROR_UNSUPPORTED_OPERATION (73)
-
此实现不支持功能。
数据类型
JVM TI 扩展了 JNI 定义的数据类型。
JVM 工具接口中使用的 JNI 类型
Type |
Description |
jboolean |
拥有 Java 编程语言 boolean 。无符号 8 位。
|
jchar |
拥有 Java 编程语言 char 。无符号 16 位。
|
jint |
拥有 Java 编程语言 int 。带符号的 32 位。
|
jlong |
拥有 Java 编程语言 long 。带符号的 64 位。
|
jfloat |
拥有 Java 编程语言 float 。 32 位。
|
jdouble |
拥有 Java 编程语言 double 。 64 位。
|
jobject |
|
jclass |
|
jvalue |
是所有原始类型和 jobject 的联合。因此,拥有任何 Java 编程语言的值。
|
jfieldID |
标识 Java 编程语言字段。 jfieldID 由 JVM 返回 可以安全地存储 TI 函数和事件。
|
jmethodID |
标识 Java 编程语言方法、初始值设定项或构造函数。 jmethodID 由 JVM 返回 可以安全地存储 TI 函数和事件。但是,如果类被卸载,它们将变得无效并且不得使用。
|
JNIEnv |
指向 JNI 函数表的指针。指向此 ( JNIEnv * ) 的指针是一个 JNI 环境。
|
JVM 工具接口基类型
Type |
Description |
jvmtiEnv |
|
jthread |
|
jthreadGroup |
typedef jobject jthreadGroup;
|
jlocation |
|
jrawMonitorID |
struct _jrawMonitorID;
typedef struct _jrawMonitorID *jrawMonitorID;
|
jvmtiError |
保存错误返回码。有关可能的值,请参阅 错误部分。
typedef enum {
JVMTI_ERROR_NONE = 0,
JVMTI_ERROR_INVALID_THREAD = 10,
...
} jvmtiError;
|
jvmtiEvent |
事件类型的标识符。有关可能的值,请参阅 Event 章节。保证本规范的未来版本永远不会指定零作为事件类型标识符。
typedef enum {
JVMTI_EVENT_SINGLE_STEP = 1,
JVMTI_EVENT_BREAKPOINT = 2,
...
} jvmtiEvent;
|
jvmtiEventCallbacks |
用于事件的回调。
typedef struct {
jvmtiEventVMInit VMInit;
jvmtiEventVMDeath VMDeath;
...
} jvmtiEventCallbacks;
有关完整结构,请参阅 事件回调。
例如,定义了 VM 初始化回调:
typedef void (JNICALL *jvmtiEventVMInit)
(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread);
请参阅各个事件以了解回调函数定义。
|
jniNativeInterface |
typedef 为 JNI 函数表 JNINativeInterface 中定义的 JNI规范 。 JNI 参考实现用下划线定义它。
typedef struct JNINativeInterface_ jniNativeInterface;
|
功能表布局
Position |
Function |
宣言 |
1 |
预订的 |
void *reserved1; |
2 |
设置事件通知模式 |
jvmtiError (JNICALL *SetEventNotificationMode) (jvmtiEnv* env,
jvmtiEventMode mode,
jvmtiEvent event_type,
jthread event_thread,
...); |
3 |
获取所有模块 |
jvmtiError (JNICALL *GetAllModules) (jvmtiEnv* env,
jint* module_count_ptr,
jobject** modules_ptr); |
4 |
获取所有线程 |
jvmtiError (JNICALL *GetAllThreads) (jvmtiEnv* env,
jint* threads_count_ptr,
jthread** threads_ptr); |
5 |
挂起线程 |
jvmtiError (JNICALL *SuspendThread) (jvmtiEnv* env,
jthread thread); |
6 |
恢复线程 |
jvmtiError (JNICALL *ResumeThread) (jvmtiEnv* env,
jthread thread); |
7 |
停止线程 |
jvmtiError (JNICALL *StopThread) (jvmtiEnv* env,
jthread thread,
jobject exception); |
8 |
中断线程 |
jvmtiError (JNICALL *InterruptThread) (jvmtiEnv* env,
jthread thread); |
9 |
获取线程信息 |
jvmtiError (JNICALL *GetThreadInfo) (jvmtiEnv* env,
jthread thread,
jvmtiThreadInfo* info_ptr); |
10 |
获取拥有的显示器信息 |
jvmtiError (JNICALL *GetOwnedMonitorInfo) (jvmtiEnv* env,
jthread thread,
jint* owned_monitor_count_ptr,
jobject** owned_monitors_ptr); |
11 |
获取当前竞争监视器 |
jvmtiError (JNICALL *GetCurrentContendedMonitor) (jvmtiEnv* env,
jthread thread,
jobject* monitor_ptr); |
12 |
运行代理线程 |
jvmtiError (JNICALL *RunAgentThread) (jvmtiEnv* env,
jthread thread,
jvmtiStartFunction proc,
const void* arg,
jint priority); |
13 |
获取顶级线程组 |
jvmtiError (JNICALL *GetTopThreadGroups) (jvmtiEnv* env,
jint* group_count_ptr,
jthreadGroup** groups_ptr); |
14 |
获取线程组信息 |
jvmtiError (JNICALL *GetThreadGroupInfo) (jvmtiEnv* env,
jthreadGroup group,
jvmtiThreadGroupInfo* info_ptr); |
15 |
获取线程组子项 |
jvmtiError (JNICALL *GetThreadGroupChildren) (jvmtiEnv* env,
jthreadGroup group,
jint* thread_count_ptr,
jthread** threads_ptr,
jint* group_count_ptr,
jthreadGroup** groups_ptr); |
16 |
获取帧数 |
jvmtiError (JNICALL *GetFrameCount) (jvmtiEnv* env,
jthread thread,
jint* count_ptr); |
17 |
获取线程状态 |
jvmtiError (JNICALL *GetThreadState) (jvmtiEnv* env,
jthread thread,
jint* thread_state_ptr); |
18 |
获取当前线程 |
jvmtiError (JNICALL *GetCurrentThread) (jvmtiEnv* env,
jthread* thread_ptr); |
19 |
获取帧位置 |
jvmtiError (JNICALL *GetFrameLocation) (jvmtiEnv* env,
jthread thread,
jint depth,
jmethodID* method_ptr,
jlocation* location_ptr); |
20 |
通知帧弹出 |
jvmtiError (JNICALL *NotifyFramePop) (jvmtiEnv* env,
jthread thread,
jint depth); |
21 |
获取局部变量 - 对象 |
jvmtiError (JNICALL *GetLocalObject) (jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jobject* value_ptr); |
22 |
获取局部变量 - Int |
jvmtiError (JNICALL *GetLocalInt) (jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jint* value_ptr); |
23 |
获取局部变量 - 长 |
jvmtiError (JNICALL *GetLocalLong) (jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jlong* value_ptr); |
24 |
获取局部变量 - Float |
jvmtiError (JNICALL *GetLocalFloat) (jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jfloat* value_ptr); |
25 |
获取局部变量 - 双精度 |
jvmtiError (JNICALL *GetLocalDouble) (jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jdouble* value_ptr); |
26 |
设置局部变量 - 对象 |
jvmtiError (JNICALL *SetLocalObject) (jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jobject value); |
27 |
设置局部变量 - Int |
jvmtiError (JNICALL *SetLocalInt) (jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jint value); |
28 |
设置局部变量 - 长 |
jvmtiError (JNICALL *SetLocalLong) (jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jlong value); |
29 |
设置局部变量 - Float |
jvmtiError (JNICALL *SetLocalFloat) (jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jfloat value); |
30 |
设置局部变量 - 双精度 |
jvmtiError (JNICALL *SetLocalDouble) (jvmtiEnv* env,
jthread thread,
jint depth,
jint slot,
jdouble value); |
31 |
创建原始监视器 |
jvmtiError (JNICALL *CreateRawMonitor) (jvmtiEnv* env,
const char* name,
jrawMonitorID* monitor_ptr); |
32 |
销毁原始监视器 |
jvmtiError (JNICALL *DestroyRawMonitor) (jvmtiEnv* env,
jrawMonitorID monitor); |
33 |
原始监视器输入 |
jvmtiError (JNICALL *RawMonitorEnter) (jvmtiEnv* env,
jrawMonitorID monitor); |
34 |
原始监视器退出 |
jvmtiError (JNICALL *RawMonitorExit) (jvmtiEnv* env,
jrawMonitorID monitor); |
35 |
原始监视器等待 |
jvmtiError (JNICALL *RawMonitorWait) (jvmtiEnv* env,
jrawMonitorID monitor,
jlong millis); |
36 |
原始监视器通知 |
jvmtiError (JNICALL *RawMonitorNotify) (jvmtiEnv* env,
jrawMonitorID monitor); |
37 |
原始监视器通知所有 |
jvmtiError (JNICALL *RawMonitorNotifyAll) (jvmtiEnv* env,
jrawMonitorID monitor); |
38 |
设置断点 |
jvmtiError (JNICALL *SetBreakpoint) (jvmtiEnv* env,
jmethodID method,
jlocation location); |
39 |
清除断点 |
jvmtiError (JNICALL *ClearBreakpoint) (jvmtiEnv* env,
jmethodID method,
jlocation location); |
40 |
获取命名模块 |
jvmtiError (JNICALL *GetNamedModule) (jvmtiEnv* env,
jobject class_loader,
const char* package_name,
jobject* module_ptr); |
41 |
设置现场访问监视 |
jvmtiError (JNICALL *SetFieldAccessWatch) (jvmtiEnv* env,
jclass klass,
jfieldID field); |
42 |
清晰的现场访问手表 |
jvmtiError (JNICALL *ClearFieldAccessWatch) (jvmtiEnv* env,
jclass klass,
jfieldID field); |
43 |
设置字段修改监视 |
jvmtiError (JNICALL *SetFieldModificationWatch) (jvmtiEnv* env,
jclass klass,
jfieldID field); |
44 |
清除字段修改手表 |
jvmtiError (JNICALL *ClearFieldModificationWatch) (jvmtiEnv* env,
jclass klass,
jfieldID field); |
45 |
是可修改类 |
jvmtiError (JNICALL *IsModifiableClass) (jvmtiEnv* env,
jclass klass,
jboolean* is_modifiable_class_ptr); |
46 |
分配 |
jvmtiError (JNICALL *Allocate) (jvmtiEnv* env,
jlong size,
unsigned char** mem_ptr); |
47 |
解除分配 |
jvmtiError (JNICALL *Deallocate) (jvmtiEnv* env,
unsigned char* mem); |
48 |
获取类签名 |
jvmtiError (JNICALL *GetClassSignature) (jvmtiEnv* env,
jclass klass,
char** signature_ptr,
char** generic_ptr); |
49 |
获取类状态 |
jvmtiError (JNICALL *GetClassStatus) (jvmtiEnv* env,
jclass klass,
jint* status_ptr); |
50 |
获取源文件名 |
jvmtiError (JNICALL *GetSourceFileName) (jvmtiEnv* env,
jclass klass,
char** source_name_ptr); |
51 |
获取类修饰符 |
jvmtiError (JNICALL *GetClassModifiers) (jvmtiEnv* env,
jclass klass,
jint* modifiers_ptr); |
52 |
获取类方法 |
jvmtiError (JNICALL *GetClassMethods) (jvmtiEnv* env,
jclass klass,
jint* method_count_ptr,
jmethodID** methods_ptr); |
53 |
获取类字段 |
jvmtiError (JNICALL *GetClassFields) (jvmtiEnv* env,
jclass klass,
jint* field_count_ptr,
jfieldID** fields_ptr); |
54 |
获取已实现的接口 |
jvmtiError (JNICALL *GetImplementedInterfaces) (jvmtiEnv* env,
jclass klass,
jint* interface_count_ptr,
jclass** interfaces_ptr); |
55 |
是接口 |
jvmtiError (JNICALL *IsInterface) (jvmtiEnv* env,
jclass klass,
jboolean* is_interface_ptr); |
56 |
是数组类 |
jvmtiError (JNICALL *IsArrayClass) (jvmtiEnv* env,
jclass klass,
jboolean* is_array_class_ptr); |
57 |
获取类加载器 |
jvmtiError (JNICALL *GetClassLoader) (jvmtiEnv* env,
jclass klass,
jobject* classloader_ptr); |
58 |
获取对象哈希码 |
jvmtiError (JNICALL *GetObjectHashCode) (jvmtiEnv* env,
jobject object,
jint* hash_code_ptr); |
59 |
获取对象监视器使用情况 |
jvmtiError (JNICALL *GetObjectMonitorUsage) (jvmtiEnv* env,
jobject object,
jvmtiMonitorUsage* info_ptr); |
60 |
获取字段名称(和签名) |
jvmtiError (JNICALL *GetFieldName) (jvmtiEnv* env,
jclass klass,
jfieldID field,
char** name_ptr,
char** signature_ptr,
char** generic_ptr); |
61 |
获取字段声明类 |
jvmtiError (JNICALL *GetFieldDeclaringClass) (jvmtiEnv* env,
jclass klass,
jfieldID field,
jclass* declaring_class_ptr); |
62 |
获取字段修饰符 |
jvmtiError (JNICALL *GetFieldModifiers) (jvmtiEnv* env,
jclass klass,
jfieldID field,
jint* modifiers_ptr); |
63 |
是场合成 |
jvmtiError (JNICALL *IsFieldSynthetic) (jvmtiEnv* env,
jclass klass,
jfieldID field,
jboolean* is_synthetic_ptr); |
64 |
获取方法名称(和签名) |
jvmtiError (JNICALL *GetMethodName) (jvmtiEnv* env,
jmethodID method,
char** name_ptr,
char** signature_ptr,
char** generic_ptr); |
65 |
获取方法声明类 |
jvmtiError (JNICALL *GetMethodDeclaringClass) (jvmtiEnv* env,
jmethodID method,
jclass* declaring_class_ptr); |
66 |
获取方法修饰符 |
jvmtiError (JNICALL *GetMethodModifiers) (jvmtiEnv* env,
jmethodID method,
jint* modifiers_ptr); |
67 |
预订的 |
void *reserved67; |
68 |
获得最大当地人 |
jvmtiError (JNICALL *GetMaxLocals) (jvmtiEnv* env,
jmethodID method,
jint* max_ptr); |
69 |
获取参数大小 |
jvmtiError (JNICALL *GetArgumentsSize) (jvmtiEnv* env,
jmethodID method,
jint* size_ptr); |
70 |
获取行号表 |
jvmtiError (JNICALL *GetLineNumberTable) (jvmtiEnv* env,
jmethodID method,
jint* entry_count_ptr,
jvmtiLineNumberEntry** table_ptr); |
71 |
获取方法位置 |
jvmtiError (JNICALL *GetMethodLocation) (jvmtiEnv* env,
jmethodID method,
jlocation* start_location_ptr,
jlocation* end_location_ptr); |
72 |
获取局部变量表 |
jvmtiError (JNICALL *GetLocalVariableTable) (jvmtiEnv* env,
jmethodID method,
jint* entry_count_ptr,
jvmtiLocalVariableEntry** table_ptr); |
73 |
设置本机方法前缀 |
jvmtiError (JNICALL *SetNativeMethodPrefix) (jvmtiEnv* env,
const char* prefix); |
74 |
设置本机方法前缀 |
jvmtiError (JNICALL *SetNativeMethodPrefixes) (jvmtiEnv* env,
jint prefix_count,
char** prefixes); |
75 |
获取字节码 |
jvmtiError (JNICALL *GetBytecodes) (jvmtiEnv* env,
jmethodID method,
jint* bytecode_count_ptr,
unsigned char** bytecodes_ptr); |
76 |
是本地方法 |
jvmtiError (JNICALL *IsMethodNative) (jvmtiEnv* env,
jmethodID method,
jboolean* is_native_ptr); |
77 |
方法是合成的 |
jvmtiError (JNICALL *IsMethodSynthetic) (jvmtiEnv* env,
jmethodID method,
jboolean* is_synthetic_ptr); |
78 |
获取加载类 |
jvmtiError (JNICALL *GetLoadedClasses) (jvmtiEnv* env,
jint* class_count_ptr,
jclass** classes_ptr); |
79 |
获取类加载器类 |
jvmtiError (JNICALL *GetClassLoaderClasses) (jvmtiEnv* env,
jobject initiating_loader,
jint* class_count_ptr,
jclass** classes_ptr); |
80 |
流行框架 |
jvmtiError (JNICALL *PopFrame) (jvmtiEnv* env,
jthread thread); |
81 |
强制提前返回 - 对象 |
jvmtiError (JNICALL *ForceEarlyReturnObject) (jvmtiEnv* env,
jthread thread,
jobject value); |
82 |
强制提前返回 - Int |
jvmtiError (JNICALL *ForceEarlyReturnInt) (jvmtiEnv* env,
jthread thread,
jint value); |
83 |
强制提前返回 - 长 |
jvmtiError (JNICALL *ForceEarlyReturnLong) (jvmtiEnv* env,
jthread thread,
jlong value); |
84 |
强制提前返回 - 浮动 |
jvmtiError (JNICALL *ForceEarlyReturnFloat) (jvmtiEnv* env,
jthread thread,
jfloat value); |
85 |
强制提前返回 - 双 |
jvmtiError (JNICALL *ForceEarlyReturnDouble) (jvmtiEnv* env,
jthread thread,
jdouble value); |
86 |
强制提前返回 - 无效 |
jvmtiError (JNICALL *ForceEarlyReturnVoid) (jvmtiEnv* env,
jthread thread); |
87 |
重新定义类 |
jvmtiError (JNICALL *RedefineClasses) (jvmtiEnv* env,
jint class_count,
const jvmtiClassDefinition* class_definitions); |
88 |
获取版本号 |
jvmtiError (JNICALL *GetVersionNumber) (jvmtiEnv* env,
jint* version_ptr); |
89 |
获得能力 |
jvmtiError (JNICALL *GetCapabilities) (jvmtiEnv* env,
jvmtiCapabilities* capabilities_ptr); |
90 |
获取源调试扩展 |
jvmtiError (JNICALL *GetSourceDebugExtension) (jvmtiEnv* env,
jclass klass,
char** source_debug_extension_ptr); |
91 |
方法是否过时 |
jvmtiError (JNICALL *IsMethodObsolete) (jvmtiEnv* env,
jmethodID method,
jboolean* is_obsolete_ptr); |
92 |
挂起线程列表 |
jvmtiError (JNICALL *SuspendThreadList) (jvmtiEnv* env,
jint request_count,
const jthread* request_list,
jvmtiError* results); |
93 |
恢复线程列表 |
jvmtiError (JNICALL *ResumeThreadList) (jvmtiEnv* env,
jint request_count,
const jthread* request_list,
jvmtiError* results); |
94 |
添加模块读取 |
jvmtiError (JNICALL *AddModuleReads) (jvmtiEnv* env,
jobject module,
jobject to_module); |
95 |
添加模块导出 |
jvmtiError (JNICALL *AddModuleExports) (jvmtiEnv* env,
jobject module,
const char* pkg_name,
jobject to_module); |
96 |
添加模块打开 |
jvmtiError (JNICALL *AddModuleOpens) (jvmtiEnv* env,
jobject module,
const char* pkg_name,
jobject to_module); |
97 |
添加模块使用 |
jvmtiError (JNICALL *AddModuleUses) (jvmtiEnv* env,
jobject module,
jclass service); |
98 |
添加模块提供 |
jvmtiError (JNICALL *AddModuleProvides) (jvmtiEnv* env,
jobject module,
jclass service,
jclass impl_class); |
99 |
是可修改模块 |
jvmtiError (JNICALL *IsModifiableModule) (jvmtiEnv* env,
jobject module,
jboolean* is_modifiable_module_ptr); |
100 |
获取所有堆栈跟踪 |
jvmtiError (JNICALL *GetAllStackTraces) (jvmtiEnv* env,
jint max_frame_count,
jvmtiStackInfo** stack_info_ptr,
jint* thread_count_ptr); |
101 |
获取线程列表堆栈跟踪 |
jvmtiError (JNICALL *GetThreadListStackTraces) (jvmtiEnv* env,
jint thread_count,
const jthread* thread_list,
jint max_frame_count,
jvmtiStackInfo** stack_info_ptr); |
102 |
获取线程本地存储 |
jvmtiError (JNICALL *GetThreadLocalStorage) (jvmtiEnv* env,
jthread thread,
void** data_ptr); |
103 |
设置线程本地存储 |
jvmtiError (JNICALL *SetThreadLocalStorage) (jvmtiEnv* env,
jthread thread,
const void* data); |
104 |
获取堆栈跟踪 |
jvmtiError (JNICALL *GetStackTrace) (jvmtiEnv* env,
jthread thread,
jint start_depth,
jint max_frame_count,
jvmtiFrameInfo* frame_buffer,
jint* count_ptr); |
105 |
预订的 |
void *reserved105; |
106 |
获取标签 |
jvmtiError (JNICALL *GetTag) (jvmtiEnv* env,
jobject object,
jlong* tag_ptr); |
107 |
设置标签 |
jvmtiError (JNICALL *SetTag) (jvmtiEnv* env,
jobject object,
jlong tag); |
108 |
强制垃圾收集 |
jvmtiError (JNICALL *ForceGarbageCollection) (jvmtiEnv* env); |
109 |
遍历从对象可达的对象 |
jvmtiError (JNICALL *IterateOverObjectsReachableFromObject) (jvmtiEnv* env,
jobject object,
jvmtiObjectReferenceCallback object_reference_callback,
const void* user_data); |
110 |
遍历可达对象 |
jvmtiError (JNICALL *IterateOverReachableObjects) (jvmtiEnv* env,
jvmtiHeapRootCallback heap_root_callback,
jvmtiStackReferenceCallback stack_ref_callback,
jvmtiObjectReferenceCallback object_ref_callback,
const void* user_data); |
111 |
遍历堆 |
jvmtiError (JNICALL *IterateOverHeap) (jvmtiEnv* env,
jvmtiHeapObjectFilter object_filter,
jvmtiHeapObjectCallback heap_object_callback,
const void* user_data); |
112 |
迭代类的实例 |
jvmtiError (JNICALL *IterateOverInstancesOfClass) (jvmtiEnv* env,
jclass klass,
jvmtiHeapObjectFilter object_filter,
jvmtiHeapObjectCallback heap_object_callback,
const void* user_data); |
113 |
预订的 |
void *reserved113; |
114 |
获取带有标签的对象 |
jvmtiError (JNICALL *GetObjectsWithTags) (jvmtiEnv* env,
jint tag_count,
const jlong* tags,
jint* count_ptr,
jobject** object_result_ptr,
jlong** tag_result_ptr); |
115 |
按照参考资料 |
jvmtiError (JNICALL *FollowReferences) (jvmtiEnv* env,
jint heap_filter,
jclass klass,
jobject initial_object,
const jvmtiHeapCallbacks* callbacks,
const void* user_data); |
116 |
遍历堆 |
jvmtiError (JNICALL *IterateThroughHeap) (jvmtiEnv* env,
jint heap_filter,
jclass klass,
const jvmtiHeapCallbacks* callbacks,
const void* user_data); |
117 |
预订的 |
void *reserved117; |
118 |
挂起所有虚拟线程 |
jvmtiError (JNICALL *SuspendAllVirtualThreads) (jvmtiEnv* env,
jint except_count,
const jthread* except_list); |
119 |
恢复所有虚拟线程 |
jvmtiError (JNICALL *ResumeAllVirtualThreads) (jvmtiEnv* env,
jint except_count,
const jthread* except_list); |
120 |
设置 JNI 函数表 |
jvmtiError (JNICALL *SetJNIFunctionTable) (jvmtiEnv* env,
const jniNativeInterface* function_table); |
121 |
获取 JNI 函数表 |
jvmtiError (JNICALL *GetJNIFunctionTable) (jvmtiEnv* env,
jniNativeInterface** function_table); |
122 |
设置事件回调 |
jvmtiError (JNICALL *SetEventCallbacks) (jvmtiEnv* env,
const jvmtiEventCallbacks* callbacks,
jint size_of_callbacks); |
123 |
生成事件 |
jvmtiError (JNICALL *GenerateEvents) (jvmtiEnv* env,
jvmtiEvent event_type); |
124 |
获取扩展函数 |
jvmtiError (JNICALL *GetExtensionFunctions) (jvmtiEnv* env,
jint* extension_count_ptr,
jvmtiExtensionFunctionInfo** extensions); |
125 |
获取扩展事件 |
jvmtiError (JNICALL *GetExtensionEvents) (jvmtiEnv* env,
jint* extension_count_ptr,
jvmtiExtensionEventInfo** extensions); |
126 |
设置扩展事件回调 |
jvmtiError (JNICALL *SetExtensionEventCallback) (jvmtiEnv* env,
jint extension_event_index,
jvmtiExtensionEvent callback); |
127 |
处理环境 |
jvmtiError (JNICALL *DisposeEnvironment) (jvmtiEnv* env); |
128 |
获取错误名称 |
jvmtiError (JNICALL *GetErrorName) (jvmtiEnv* env,
jvmtiError error,
char** name_ptr); |
129 |
获取 JLocation 格式 |
jvmtiError (JNICALL *GetJLocationFormat) (jvmtiEnv* env,
jvmtiJlocationFormat* format_ptr); |
130 |
获取系统属性 |
jvmtiError (JNICALL *GetSystemProperties) (jvmtiEnv* env,
jint* count_ptr,
char*** property_ptr); |
131 |
获取系统属性 |
jvmtiError (JNICALL *GetSystemProperty) (jvmtiEnv* env,
const char* property,
char** value_ptr); |
132 |
设置系统属性 |
jvmtiError (JNICALL *SetSystemProperty) (jvmtiEnv* env,
const char* property,
const char* value_ptr); |
133 |
获取阶段 |
jvmtiError (JNICALL *GetPhase) (jvmtiEnv* env,
jvmtiPhase* phase_ptr); |
134 |
获取当前线程 CPU 计时器信息 |
jvmtiError (JNICALL *GetCurrentThreadCpuTimerInfo) (jvmtiEnv* env,
jvmtiTimerInfo* info_ptr); |
135 |
获取当前线程 CPU 时间 |
jvmtiError (JNICALL *GetCurrentThreadCpuTime) (jvmtiEnv* env,
jlong* nanos_ptr); |
136 |
获取线程 CPU 定时器信息 |
jvmtiError (JNICALL *GetThreadCpuTimerInfo) (jvmtiEnv* env,
jvmtiTimerInfo* info_ptr); |
137 |
获取线程 CPU 时间 |
jvmtiError (JNICALL *GetThreadCpuTime) (jvmtiEnv* env,
jthread thread,
jlong* nanos_ptr); |
138 |
获取定时器信息 |
jvmtiError (JNICALL *GetTimerInfo) (jvmtiEnv* env,
jvmtiTimerInfo* info_ptr); |
139 |
获取时间 |
jvmtiError (JNICALL *GetTime) (jvmtiEnv* env,
jlong* nanos_ptr); |
140 |
获得潜在能力 |
jvmtiError (JNICALL *GetPotentialCapabilities) (jvmtiEnv* env,
jvmtiCapabilities* capabilities_ptr); |
141 |
预订的 |
void *reserved141; |
142 |
添加功能 |
jvmtiError (JNICALL *AddCapabilities) (jvmtiEnv* env,
const jvmtiCapabilities* capabilities_ptr); |
143 |
放弃能力 |
jvmtiError (JNICALL *RelinquishCapabilities) (jvmtiEnv* env,
const jvmtiCapabilities* capabilities_ptr); |
144 |
获取可用的处理器 |
jvmtiError (JNICALL *GetAvailableProcessors) (jvmtiEnv* env,
jint* processor_count_ptr); |
145 |
获取类版本号 |
jvmtiError (JNICALL *GetClassVersionNumbers) (jvmtiEnv* env,
jclass klass,
jint* minor_version_ptr,
jint* major_version_ptr); |
146 |
获取常量池 |
jvmtiError (JNICALL *GetConstantPool) (jvmtiEnv* env,
jclass klass,
jint* constant_pool_count_ptr,
jint* constant_pool_byte_count_ptr,
unsigned char** constant_pool_bytes_ptr); |
147 |
获取环境本地存储 |
jvmtiError (JNICALL *GetEnvironmentLocalStorage) (jvmtiEnv* env,
void** data_ptr); |
148 |
设置环境本地存储 |
jvmtiError (JNICALL *SetEnvironmentLocalStorage) (jvmtiEnv* env,
const void* data); |
149 |
添加到 Bootstrap 类加载器搜索 |
jvmtiError (JNICALL *AddToBootstrapClassLoaderSearch) (jvmtiEnv* env,
const char* segment); |
150 |
设置详细标志 |
jvmtiError (JNICALL *SetVerboseFlag) (jvmtiEnv* env,
jvmtiVerboseFlag flag,
jboolean value); |
151 |
添加到系统类加载器搜索 |
jvmtiError (JNICALL *AddToSystemClassLoaderSearch) (jvmtiEnv* env,
const char* segment); |
152 |
重新转换类 |
jvmtiError (JNICALL *RetransformClasses) (jvmtiEnv* env,
jint class_count,
const jclass* classes); |
153 |
获取拥有的监视器堆栈深度信息 |
jvmtiError (JNICALL *GetOwnedMonitorStackDepthInfo) (jvmtiEnv* env,
jthread thread,
jint* monitor_info_count_ptr,
jvmtiMonitorStackDepthInfo** monitor_info_ptr); |
154 |
获取对象大小 |
jvmtiError (JNICALL *GetObjectSize) (jvmtiEnv* env,
jobject object,
jlong* size_ptr); |
155 |
获取本地实例 |
jvmtiError (JNICALL *GetLocalInstance) (jvmtiEnv* env,
jthread thread,
jint depth,
jobject* value_ptr); |
156 |
设置堆采样间隔 |
jvmtiError (JNICALL *SetHeapSamplingInterval) (jvmtiEnv* env,
jint sampling_interval); |
事件
处理事件
代理可以获知应用程序中发生的许多事件。
要处理事件,请使用 SetEventCallbacks
指定一组回调函数。对于每个事件,将调用相应的回调函数。回调函数的参数提供有关事件的附加信息。
回调函数通常在应用程序线程中调用。虚拟机 TI 实现不会以任何方式对事件进行排队。这意味着必须仔细编写事件回调函数。以下是一些一般准则。请参阅个别事件描述以获取更多建议。
- 在执行事件回调期间抛出的任何异常都可以重写当前应用程序线程中任何当前挂起的异常。当事件回调进行可能生成异常的 JNI 调用时,必须注意保留挂起的异常。
- 事件回调函数必须是可重入的。虚拟机 TI 实现不对事件进行排队。如果代理需要一次处理一个事件,它可以在事件回调函数中使用原始监视器来序列化事件处理。
- 执行JNI的FindClass函数加载类的事件回调函数需要注意FindClass定位的是当前native方法关联的类加载器。出于类加载的目的,包含 JNI 环境作为回调参数的事件回调将被视为本地调用,其中本地方法位于事件线程当前框架的类中。
一些JVM TI 事件标识具有 JNI 引用的对象。 JVM 中的所有引用 TI事件是JNI本地引用,事件回调返回后失效。除非另有说明,事件回调中发送的指针所引用的内存在事件回调返回后可能不会被引用。
除非另有说明,否则事件将在导致事件的线程上传递。事件在发生时发送。每个事件的规范包括可以发送它的 阶段 集合;如果事件触发活动发生在另一个阶段,则不会发送任何事件。
产生事件的线程不会改变它的执行状态(例如,事件不会导致线程被挂起)。如果代理希望事件导致挂起,则代理负责使用 SuspendThread
显式挂起线程。
如果在多个环境中启用了一个事件,该事件将按照环境创建的顺序发送到每个代理。
启用事件
所有事件最初都是禁用的。为了接收任何事件:
多个同地活动
在许多情况下,一个线程中的同一位置可能会发生多个事件。发生这种情况时,将按照本节中指定的顺序通过事件回调报告所有事件。
如果当前位置位于方法的入口点,则MethodEntry
事件在同一线程中当前位置的任何其他事件之前报告。
如果在当前位置检测到异常捕获,要么是因为它是 catch 子句的开头,要么是清除挂起异常的本机方法已返回,exceptionCatch
事件将在同一位置的任何其他事件之前报告线。
如果 singleStep
事件或 breakpoint
事件在当前位置被触发,则该事件被定义为在执行当前位置的代码之前立即发生。这些事件在同一线程的当前位置执行代码触发的任何事件之前报告(特别是:exception
、fieldAccess
和 fieldModification
)。如果同一线程和位置同时触发了步进事件和断点事件,则步进事件在断点事件之前报告。
如果当前位置是方法的出口点(即返回调用者之前的最后一个位置),MethodExit
事件和 FramePop
事件(如果请求)在同一线程中当前位置的所有其他事件之后报告.这两个事件彼此之间没有特定的顺序。
在同一线程中同一位置的代理处理某些其他事件期间,可以触发并置事件。如果这样的事件,类型y, 在处理类型事件期间触发x, 而如果x先于y在上面指定的顺序中,同一地点的事件y报告当前线程和位置。如果x不先于y, y未报告当前线程和位置。例如,如果在 SingleStep
的处理过程中在当前位置设置了断点,则在线程离开当前位置之前将报告该断点。
以下事件永远不会被视为与其他事件位于同一地点。
事件回调
下面的事件回调结构用于指定事件的处理函数。它通过SetEventCallbacks
函数设置。
typedef struct {
jvmtiEventVMInit VMInit;
jvmtiEventVMDeath VMDeath;
jvmtiEventThreadStart ThreadStart;
jvmtiEventThreadEnd ThreadEnd;
jvmtiEventClassFileLoadHook ClassFileLoadHook;
jvmtiEventClassLoad ClassLoad;
jvmtiEventClassPrepare ClassPrepare;
jvmtiEventVMStart VMStart;
jvmtiEventException Exception;
jvmtiEventExceptionCatch ExceptionCatch;
jvmtiEventSingleStep SingleStep;
jvmtiEventFramePop FramePop;
jvmtiEventBreakpoint Breakpoint;
jvmtiEventFieldAccess FieldAccess;
jvmtiEventFieldModification FieldModification;
jvmtiEventMethodEntry MethodEntry;
jvmtiEventMethodExit MethodExit;
jvmtiEventNativeMethodBind NativeMethodBind;
jvmtiEventCompiledMethodLoad CompiledMethodLoad;
jvmtiEventCompiledMethodUnload CompiledMethodUnload;
jvmtiEventDynamicCodeGenerated DynamicCodeGenerated;
jvmtiEventDataDumpRequest DataDumpRequest;
jvmtiEventReserved reserved72;
jvmtiEventMonitorWait MonitorWait;
jvmtiEventMonitorWaited MonitorWaited;
jvmtiEventMonitorContendedEnter MonitorContendedEnter;
jvmtiEventMonitorContendedEntered MonitorContendedEntered;
jvmtiEventReserved reserved77;
jvmtiEventReserved reserved78;
jvmtiEventReserved reserved79;
jvmtiEventResourceExhausted ResourceExhausted;
jvmtiEventGarbageCollectionStart GarbageCollectionStart;
jvmtiEventGarbageCollectionFinish GarbageCollectionFinish;
jvmtiEventObjectFree ObjectFree;
jvmtiEventVMObjectAlloc VMObjectAlloc;
jvmtiEventReserved reserved85;
jvmtiEventSampledObjectAlloc SampledObjectAlloc;
jvmtiEventVirtualThreadStart VirtualThreadStart;
jvmtiEventVirtualThreadEnd VirtualThreadEnd;
} jvmtiEventCallbacks;
事件索引
一小步
void JNICALL
SingleStep(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jlocation location)
单步事件允许代理以 VM 允许的最细粒度跟踪线程执行。每当线程到达新位置时,都会生成单步事件。通常,单步事件代表一个 VM 指令的完成,如定义在Java™ 虚拟机规范.但是,某些实现可能会以不同方式定义位置。在任何情况下,method
和 location
参数唯一标识当前位置,并在该信息可用时允许映射到源文件和行号。
本机方法内不会生成任何单步事件。
仅在直播阶段发送
JVMTI_EVENT_SINGLE_STEP
60
1.0
参数
Name |
Type |
Description |
jni_env |
JNIEnv * |
事件(当前)线程的 JNI 环境 |
thread |
jthread |
即将执行新指令的线程 |
method |
|