soho95的专栏

Java 程序的诊断和调试 对于Java 程序的诊断和调试不同需求有不同的解决方案。

如果java的总体的运行状况,可以直接使用 JDK 提供的 jconsole 程序。

如果希望提高程序的执行效率,开发人员可以使用各种 Java Profiler。

如果是内存溢出错误(OutOfMemoryError)等等,这时可以把当前的内存输出到 Dump 文件,再使用堆分析器或者 Dump 文件分析器等工具进行研究,查看当前运行态堆(Heap)中存在的实例整体状况来诊断问题。

共同的特点,通过和虚拟机进行交互,发现 Java 程序运行的问题。

JDK 本身定义 API 来与虚拟机直接交互,这些 API 能很方便的进行扩展。

JVMTI 的简介

JVMTI(JVM Tool Interface)是 Java 虚拟机所提供的 native 编程接口,是 JVMPI(Java Virtual Machine Profiler Interface)和 JVMDI(Java Virtual Machine Debug Interface)的更新版本。JVMTI 提供了可用于 debug 和 profiler 的接口;同时,在 Java 5/6 中,虚拟机接口也增加了监听(Monitoring),线程分析(Thread analysis)以及覆盖率分析(Coverage Analysis)等功能。JVMTI是实现 Java 调试器,以及其它 Java 运行态测试与分析工具的基础。

JVMTI 并非在所有的 Java 虚拟机上都有实现,不同虚拟机的实现也不尽相同。但是主流的虚拟机中,比如 Sun 和 IBM,以及一些开源的如 Apache Harmony DRLVM 中,都提供了标准 JVMTI 实现。JVMTI 是一套本地代码接口,因此使用 JVMTI 需要我们与 C/C++ 以及 JNI 打交道。事实上,开发时一般采用建立一个 Agent 的方式来使用 JVMTI,它使用 JVMTI 函数,设置一些回调函数,并从 Java 虚拟机中得到当前的运行态信息,并作出自己的判断,最后还可能操作虚拟机的运行态。把 Agent 编译成一个动态链接库之后,我们就可以在 Java 程序启动的时候来加载它(启动加载模式),也可以在 Java 5 之后使用运行时加载(活动加载模式)。

-agentlib:agent-lib-name=options -agentpath:path-to-agent=options

Agent 的工作过程

Agent 是在虚拟机启动之时加载的,这个加载处于虚拟机初始化的早期,在这个时间点上:

所有的 Java 类都未被初始化; 所有的对象实例都未被创建; 因而,没有任何 Java 代码被执行;

但在这个时候,我们已经可以:

操作 JVMTI 的 Capability 参数; 使用系统参数; 动态库被加载之后,虚拟机会先寻找一个 Agent 入口函数:

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)

在这个函数中,虚拟机传入了一个 JavaVM 指针,以及命令行的参数。通过 JavaVM,我们可以获得 JVMTI 的指针,并获得 JVMTI 函数的使用能力,所有的 JVMTI 函数都通过这个 jvmtiEnv 获取,不同的虚拟机实现提供的函数细节可能不一样,但是使用的方式是统一的。

jvmtiEnv *jvmti; (*jvm)->GetEnv(jvm, &jvmti, JVMTI_VERSION_1_0);

这里传入的版本信息参数很重要,不同的 JVMTI 环境所提供的功能以及处理方式都可能有所不同,不过它在同一个虚拟机中会保持不变。命令行参数事实上就是上面启动命令行中的 options 部分,在 Agent 实现中需要进行解析并完成后续处理工作。参数传入的字符串仅仅在 Agent_OnLoad 函数里有效,如果需要长期使用,开发者需要做内存的复制工作,同时在最后还要释放这块存储。另外,有些 JDK 的实现会使用 JAVA_TOOL_OPTIONS 所提供的参数,这个常见于一些嵌入式的 Java 虚拟机(不使用命令行)。需要强调的是,这个时候由于虚拟机并未完成初始化工作,并不是所有的 JVMTI 函数都可以被使用。

Agent 还可以在运行时加载,如果您了解 Java Instrument 模块,您一定对它的运行态加载有印象,这个新功能事实上也是 Java Agent 的一个实现。具体说来,虚拟机会在运行时监听并接受 Agent 的加载,在这个时候,它会使用 Agent 的:

JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char *options, void *reserved);

同样的在这个初始化阶段,不是所有的 JVMTI 的 Capability 参数都处于可操作状态,而且 options 这个 char 数组在这个函数运行之后就会被丢弃,如果需要,需要做好保留工作。

Agent 的主要功能是通过一系列的在虚拟机上设置的回调(callback)函数完成的,一旦某些事件发生,Agent 所设置的回调函数就会被调用,来完成特定的需求。

卸载

Agent 完成任务,或者虚拟机关闭的时候,虚拟机都会调用一个类似于类析构函数的方法来完成最后的清理任务,注意这个函数和虚拟机自己的 VM_DEATH 事件是不同的。

JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)

JVMTI 的环境和错误处理我们使用 JVMTI 的过程,主要是设置 JVMTI 环境,监听虚拟机所产生的事件,以及在某些事件上加上我们所希望的回调函数。

JVMTI 环境

接受失败等于回归真实的自我,

soho95的专栏

相关文章:

你感兴趣的文章:

标签云: