JPDA(Java Platform Debugger Architecture)是 Java 平台调试体系结构的缩写通过 JPDA 提供的 API开发人员可以方便灵活的搭建 Java 调试应用程序 JPDA 主要由三个部分组成Java 虚拟机工具接口(JVMTI)Java 调试线协议(JDWP)以及 Java 调试接口(JDI)本系列将会详细介绍这三个模块的内部细节通过实例为读者揭开 JPDA 的面纱本文是该系列的第一篇将会着重从整体上介绍 JPDA 的各个组成阐述它们彼此之间的内在关联
JPDA 概述
所有的程序员都会遇到 bug对于运行态的错误我们往往需要一些方法来观察和测试运行态中的环境在 Java 程序中最简单的您是否尝试过使用 Systemoutprintln() 来输出您的 Java 程序的执行中的各种变量状态来发现您的 Java 程序运行时的问题?这种方式方便易用在一些简单的情况下能够解决您的问题但是如果当您的程序运行在远程环境上或者当前环境不允许控制台终端输出(比如考虑一下虚拟机初始化之时)您无法获取终端输出的时候呢?或者如果您根本无法本地修改运行您的程序?
无须担心您可以通过很多的调试工具来帮助您解决这个问题常见的 IDE 都附带一个非常直观简单的调试工具比如 Eclipse(图 )就提供一个功能非常全面操作非常简单的调试器
图 使用 Eclipse 调试 Java 程序
其他的一些常见的 Java IDE比如 Netbeans 和 IntelliJ 等等也都提供了类似的功能您甚至能不用 IDE 提供的图形界面使用 JDK 自带的 jdb 工具以文本命令的形式来调试您的 Java 程序这些形形色色的调试器都支持本地和远程的程序调试那么它们是如何被开发的?它们之间存在着什么样的联系呢?我们不得不提及 Java 的调试体系—— JPDA
我们知道Java 程序都是运行在 Java 虚拟机上的我们要调试 Java 程序事实上就需要向 Java 虚拟机请求当前运行态的状态并对虚拟机发出一定的指令设置一些回调等等那么 Java 的调试体系就是虚拟机的一整套用于调试的工具和接口
对于 Java 虚拟机接口熟悉的人来说您一定还记得 Java 提供了两个接口体系JVMPI(Java Virtual Machine Profiler Interface)和 JVMDI(Java Virtual Machine Debug Interface)而它们以及在 Java SE 中准备代替它们的 JVMTI(Java Virtual Machine Tool Interface)都是 Java 平台调试体系(Java Platform Debugger ArchitectureJPDA)的重要组成部分 Java SE 自 版就开始推出 Java 平台调试体系结构(JPDA)工具集而从 JDK x 开始Java SDK 就提供了对 Java 平台调试体系结构的直接支持顾名思义这个体系为开发人员提供了一整套用于调试 Java 程序的 API是一套用于开发 Java 调试工具的接口和协议本质上说它是我们通向虚拟机考察虚拟机运行态的一个通道一套工具理解这一点对于学习 JPDA 非常重要
换句话说通过 JPDA 这套接口我们就可以开发自己的调试工具通过这些 JPDA 提供的接口和协议调试器开发人员就能根据特定开发者的需求扩展定制 Java 调试应用程序开发出吸引开发人员使用的调试工具前面我们提到的 IDE 调试工具都是基于 JPDA 体系开发的区别仅仅在于它们可能提供了不同的图形界面具有一些不同的自定义功能另外我们要注意的是JPDA 是一套标准任何的 JDK 实现都必须完成这个标准因此通过 JPDA 开发出来的调试工具先天具有跨平台不依赖虚拟机实现JDK 版本无关等移植优点因此大部分的调试工具都是基于这个体系的
JPDA 组成模块
JPDA 定义了一个完整独立的体系它由三个相对独立的层次共同组成而且规定了它们三者之间的交互方式或者说定义了它们通信的接口这三个层次由低到高分别是 Java 虚拟机工具接口(JVMTI)Java 调试线协议(JDWP)以及 Java 调试接口(JDI)这三个模块把调试过程分解成几个很自然的概念调试者(debugger)和被调试者(debuggee)以及他们中间的通信器被调试者运行于我们想调试的 Java 虚拟机之上它可以通过 JVMTI 这个标准接口监控当前虚拟机的信息调试者定义了用户可使用的调试接口通过这些接口用户可以对被调试虚拟机发送调试命令同时调试者接受并显示调试结果在调试者和被调试着之间调试命令和调试结果都是通过 JDWP 的通讯协议传输的所有的命令被封装成 JDWP 命令包通过传输层发送给被调试者被调试者接收到 JDWP 命令包后解析这个命令并转化为 JVMTI 的调用在被调试者上运行类似的JVMTI 的运行结果被格式化成 JDWP 数据包发送给调试者并返回给 JDI 调用而调试器开发人员就是通过 JDI 得到数据发出指令图 展示了这个过程
图 JPDA 模块层次
当然开发人员完全可以不使用完整的三个层次而是基于其中的某一个层次开发自己的应用比如您完全可以仅仅依靠通过 JVMTI 函数开发一个调试工具而不使用 JDWP 和 JDI只使用自己的通讯和命令接口当然除非是有特殊的需求利用已有的实现会使您事半功倍避免重复发明轮子
这三个模块我们会在后续文章中分别详细介绍这里我们简单介绍它们的主要功能
Java 虚拟机工具接口(JVMTI)
JVMTI(Java Virtual Machine Tool Interface)即指 Java 虚拟机工具接口它是一套由虚拟机直接提供的 native 接口它处于整个 JPDA 体系的最底层所有调试功能本质上都需要通过 JVMTI 来提供通过这些接口开发人员不仅调试在该虚拟机上运行的 Java 程序还能查看它们运行的状态设置回调函数控制某些环境变量从而优化程序性能我们知道JVMTI 的前身是 JVMDI 和 JVMPI它们原来分别被用于提供调试 Java 程序以及 Java 程序调节性能的功能在 JSE 之后 JDK 取代了 JVMDI 和 JVMPI 这两套接口JVMDI 在最新的 Java SE 中已经不提供支持而 JVMPI 也计划在 Java SE 后被彻底取代
Java 调试线协议(JDWP)
JDWP(Java Debug Wire Protocol)是一个为 Java 调试而设计的一个通讯交互协议它定义了调试器和被调试程序之间传递的信息的格式在 JPDA 体系中作为前端(frontend)的调试者(debugger)进程和后端(backend)的被调试程序(debuggee)进程之间的交互数据的格式就是由 JDWP 来描述的它详细完整地定义了请求命令回应数据和错误代码保证了前端和后端的 JVMTI 和 JDI 的通信通畅比如在 Sun 公司提供的实现中它提供了一个名为 jdwpdll(jdwpso)的动态链接库文件这个动态库文件实现了一个 Agent它会负责解析前端发出的请求或者命令并将其转化为 JVMTI 调用然后将 JVMTI 函数的返回值封装成 JDWP 数据发还给后端
另外这里需要注意的是 JDWP 本身并不包括传输层的实现传输层需要独立实现但是 JDWP 包括了和传输层交互的严格的定义就是说JDWP 协议虽然不规定我们是通过 EMS 还是快递运送货物的但是它规定了我们传送的货物的摆放的方式在 Sun 公司提供的 JDK 中在传输层上它提供了 socket 方式以及在 Windows 上的 shared memory 方式当然传输层本身无非就是本机内进程间通信方式和远端通信方式用户有兴趣也可以按 JDWP 的标准自己实现
Java 调试接口(JDI)
JDI(Java Debug Interface)是三个模块中最高层的接口在多数的 JDK 中它是由 Java 语言实现的 JDI 由针对前端定义的接口组成通过它调试工具开发人员就能通过前端虚拟机上的调试器来远程操控后端虚拟机上被调试程序的运行JDI 不仅能帮助开发人员格式化 JDWP 数据而且还能为 JDWP 数据传输提供队列缓存等优化服务从理论上说开发人员只需使用 JDWP 和 JVMTI 即可支持跨平台的远程调试但是直接编写 JDWP 程序费时费力而且效率不高因此基于 Java 的 JDI 层的引入简化了操作提高了开发人员开发调试程序的效率
表 总结了三个模块的不同点
表 JPDA 层次比较
模块 | 层次 | 编程语言 | 作用 | JVMTI | 底层C获取及控制当前虚拟机状态
JDWP | 中介层C定义 JVMTI 和 JDI 交互的数据格式
JDI | 高层Java提供 Java API 来远程控制被调试虚拟机
JPDA 实现
关于 Apache Harmony 项目
Apache Harmony 旨在开发出一个独立且与现有 JDK 兼容的 Java SE 实现它以 Apache 软件许可证 版发行它建立了一个开放的模块化运行时架构包括虚拟机和类库之间及其内部的模块化通过这个平台社区能在已有实现的基础上自由定制自己的 Java 实现或者对某个模块单独进行创新
每一个虚拟机都应该实现 JVMTI 接口但是 JDWP 和 JDI 本身与虚拟机并非是不可分的这三个层之间是通过标准所定义的交互的接口和协议联系起来的因此它们可以被独立替换或取代但不会影响到整体调试工具的开发和使用因此开发和使用自己的 JDWP 和 JDI 接口实现是可能的
Java 软件开发包(SDK)标准版里提供了 JPDA 三个层次的标准实现事实上调试工具开发人员还有很多其他开源实现可以选择比如 Apache Harmony 提供了 JDWP 的实现而 JDI我们可以在 Eclipse 一个子项目 orgeclipsejdtdebug 里找到其完整的实现(Harmony 也使用了这套实现作为其 JSE 类库的一部分)通过标准协议Eclipse IDE 的调试工具就可以完全在 Harmony 的环境上运行
Java 调试接口的特点
Java 语言是第一个使用虚拟机概念的流行的编程语言正是因为虚拟机的存在使很多事情变得简单而轻松掌握了虚拟机就掌握了内存分配线程管理即时优化等等运行态同样的Java 调试的本质就是和虚拟机打交道通过操作虚拟机来达到观察调试我们自己代码的目的这个特点决定了 Java 调试接口和以前其他编程语言的巨大区别
以C/C++ 的调试为例目前比较流行的调试工具是 GDB 和微软的 Visual Studio 自带的 debugger在这种 debugger 中首先我们必须编译一个 debug 模式的程序这个会比实际的 release 模式程序大很多其次在调试过程中debugger 将会深层接入程序的运行掌握和控制运行态的一些信息并将这些信息及时返回这种介入对运行的效率和内存占用都有一定的需求基于这些需求这些 Debugger 本身事实上是提供了或者说创建和管理了一个运行态因此他们的程序算法比较复杂个头都比较大对于远端的调试GDB 也没有很好的默认实现当然C/C++ 在这方面也没有特别大的需求
而Java 则不同由于 Java 的运行态已经被虚拟机所很好地管理因此作为 Java 的 Debugger 无需再自己创造一个可控的运行态而仅仅需要去操作虚拟机就可以了 Java 的 JPDA 就是一套为调试和优化服务的虚拟机的操作工具其中JVMTI 是整合在虚拟机中的接口JDWP 是一个通讯层而 JDI 是前端为开发人员准备好的工具和运行库
从构架上说我们可以把 JPDA 看作成是一个 C/S 体系结构的应用在这个构架下我们可以方便地通过网络在任意的地点调试另外一个虚拟机上的程序这个就很好地解决了部署和测试的问题尤其满足解决了很多网络时代中的开发应用的需求前端和后端的分离也方便用户开发适合于自己的调试工具
从效率上看由于 Java 程序本身就是编译成字节码运行在虚拟机上的因此调试前后的程序内存占用都不会有大变化(仅仅是启动一个 JDWP 所需要的内存)任意程度都可以很好地调试非常方便而 JPDA 构架下的几个组成部分JDWP 和 JDI 都比较小主要的工作可以让虚拟机自己完成
从灵活性上Java 调试工具是建立在强大的虚拟机上的因此很多前沿的应用比如动态编译运行字节码的实时替换等等都可以通过对虚拟机的改进而得到实现随着虚拟机技术的逐步发展和深入各种不同种类不同应用领域中虚拟机的出现各种强大的功能的加入给我们的调试工具也带来很多新的应用
总而言之一个先天的可控的运行态给 Java 的调试工作给 Java 调试接口带来了极大的优势和便利通过 JPDA 这个标准我们可以从虚拟机中得到我们所需要的信息完成我们所希望的操作更好地开发我们的程序
结束语
本文简单介绍了 JPDA 的三个模块以及它们如何和其它层次交互让读者在整体上对 JPDA 体系有了一个直观的了解从而方便后面针对每个模块具体介绍的学习这里我们学习到
JPDA 定义了一套如何开发调试工具的接口和规范
JPDA 由三个独立的模块 JVMTIJDWPJDI 组成
调试者通过 JDI 发送接受调试命令
JDWP 定义调试者和被调试者交流数据的格式
JVMTI 可以控制当前虚拟机运行状态
除了标准实现JPDA 还有许多开源实现供使用
Java 调试工具的优点