Welcome 微信登录

首页 / 软件开发 / JAVA / 深入Java调试体系,第2部分: JVMTI和Agent实现

深入Java调试体系,第2部分: JVMTI和Agent实现2011-02-14 IBM 吕晶 邱小侠JPDA(Java Platform Debugger Architecture)是 Java 平台调试体系结构的缩写。通过 JPDA 提供的 API,开发人员可以方便灵活的搭建 Java 调试应用程序。 JPDA 主要由三个部分组成:Java 虚拟机工具接口(JVMTI)、Java 调试线协议(JDWP),以及 Java 调试接口(JDI)。本系列将会详细介绍这三个模块的内部细节,并通过实例为读者揭开 JPDA 的面纱。

本系列的 第 1 部分 从整体上介绍 JPDA 的各个组成,以及它们彼此之间的内在关联。本文是该系列的第 2 篇,将会着重介绍强大的虚拟机接口 - JVMTI,以及如何使用 JVMTI 编写用户自定义的 Java 调试和诊断程序。

Java 程序的诊断和调试

开发人员对 Java 程序的诊断和调试有许多不同种类、不同层次的需求,这就使得开发人员需要使用不同的工具来解决问题。比如,在 Java 程序运行的过程中,程序员希望掌握它总体的运行状况,这个时候程序员可以直接使用 JDK 提供的 jconsole 程序。如果希望提高程序的执行效率,开发人员可以使用各种 Java Profiler。这种类型的工具非常多,各有优点,能够帮助开发人员找到程序的瓶颈,从而提高程序的运行速度。开发人员还会遇到一些与内存相关的问题,比如内存占用过多,大量内存不能得到释放,甚至导致内存溢出错误(OutOfMemoryError)等等,这时可以把当前的内存输出到 Dump 文件,再使用堆分析器或者 Dump 文件分析器等工具进行研究,查看当前运行态堆(Heap)中存在的实例整体状况来诊断问题。所有这些工具都有一个共同的特点,就是最终他们都需要通过和虚拟机进行交互,来发现 Java 程序运行的问题。

已有的这些工具虽然强大易用,但是在一些高级的应用环境中,开发者常常会有一些特殊的需求,这个时候就需要定制工具来达成目标。 JDK 本身定义了目标明确并功能完善的 API 来与虚拟机直接交互,而且这些 API 能很方便的进行扩展,从而满足开发者各式的需求。在本文中,将比较详细地介绍 JVMTI,以及如何使用 JVMTI 编写一个定制的 Agent 。

Agent
Agent 即 JVMTI 的客户端,它和执行 Java 程序的虚拟机运行在同一个进程上,因此通常他们的实现都很紧凑,他们通常由另一个独立的进程控制,充当这个独立进程和当前虚拟机之间的中介,通过调用 JVMTI 提供的接口和虚拟机交互,负责获取并返回当前虚拟机的状态或者转发控制命令。

JVMTI 的简介

JVMTI(JVM Tool Interface)是 Java 虚拟机所提供的 native 编程接口,是 JVMPI(Java Virtual Machine Profiler Interface)和 JVMDI(Java Virtual Machine Debug Interface)的更新版本。从这个 API 的发展历史轨迹中我们就可以知道,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 类都未被初始化;

所有的对象实例都未被创建;

因而,没有任何 Java 代码被执行;

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

操作 JVMTI 的 Capability 参数;