JPDA 架构研究2 - JDWP代理

引入:

我们先从最重要的通信层讲起。通信层(JDWP) ,它的全称是Java Debug Wire Protocol。从"Wire"这词就可以看出,它主要是起到"连线”的作用,也就是说,它主要是起到把最靠近程序员的JDI(也就是调试器)和最靠近运行在虚拟机中的程序的JVMTI 连接在一起。因为彼此之间语言不通。比如说JDI这层,我们主要用的eclipse,它是JAVA语言编写的。而JVMTI是用来查询虚拟机状态的,而JAVA虚拟机是用C/C++写的,所以在语言不通的情况下,就必须有个中间过程来充当翻译。 这样就产生了 Agent 这个职位。它就是用来把从JDI发来的数据包双向转换到JVMTI对虚拟机的函数调用。

实践:

我们这里先来看下JDWP的Agent.

当明白了代理的作用后,我们来找JDWP的实现。在Sun的JDK中,我们找到了一个DLL文件叫jdwp.dll (Linux环境则是jdwp.so) .  它位于 $JAVA_HOME/jre/bin目录下。我们用exeScope软件打开查看内容:

显然,它是Oracle的Sun的JDK提供的jdwp ,它提供了2个方法,一个是_Agent_OnLoad(),一个是_Agent_OnUnload()

这两个接口是用来和JVM交互用的。其中当代理被JVM加载时,会调用_Agent_Onload方法,而代理被JVM卸载时候,会调用_Agent_OnUnLoad方法。

因为我们在远程调试时候启动JVM时候加了代理参数:

java -agentlib:<agentLibName> ,而agentLibName我们配置的是jdwp, 所以它就对应上jdwp.dll.

也就是远程调试时候自动会在启动target VM时候启用jdwp这个代理库。

总结:

从上过程我们似乎可以总结一些结论:

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

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

(从这点上说,最明显的好处就是它能完成早期调试中用System.out.println()无法解决的问题,因为System.out.println()前提是代码行所在的类已经被初始化过了)

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

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

时间: 2024-11-13 06:32:39

JPDA 架构研究2 - JDWP代理的相关文章

JPDA 架构研究3 - JDWP层的数据包

引入: 现在我们来看JDWP层的数据包.源码在JDK中很容易找到,它定义$JAVA_HOME/include/jdwpTransport.h 头文件中. 分析: Part 1: 握手过程 (handshake ) 握手包发生在Debugger(JDI端)和Target VM(JVMTI端)的传输层连接建立,并且在发送任何实际数据报之前完成的.它过程如下: a. Debugger会发送14个字节的握手请求到Target VM,这个包的内容是14字节的ASCII字符串 "JDWP-Handshake

JPDA 架构研究4 - JDWP的传输接口

引入: 上一篇文章主要分析JDWP层传输的数据包的格式,这篇文章我们主要关注于JDWP层是如何传输的. 分析: 传输的具体实现是根据各个JVM自己实现,但是他们有个共同点就是都是用C/C++实现的,并且都实现了jdwpTransport.h (和上篇一样的这个文件).实现根据我们的知识可以得知是以dll文件(或者Unix平台下是so文件)的形式存在.因为我们从前面了解到,JDWP层有Agent负责在数据包和JVMTI的函数调用之间转换,所以顺理成章知道,传输层的DLL文件也必定会有一个onloa

JPDA 架构研究1- 整体架构

引入: 关于JPDA(Java Platform Debugger Architecture) 网上有很多讲解,例子.我也不重复发明轮子了,这组文章主要从实践的代码分析角度,仔细剖析这个平台的特点,以及这其中的一些问题澄清和误区. 实践: 如果我们从利用Java Remote Debug的功能来看待JPDA ,并且使用socket的通信方式的话,那么整个过程应该如下: 首先,你在要被调试的服务器上开启远程调试功能.以tomcat 为例: 你要么用命令catalina jpda start,它会启

JPDA 架构研究17 - JDI概览

引入: 前面用了很多篇幅来讨论JVMTI和JDWP部分,现在终于来看最靠近用户端部分了,JDI. 分析: a. JDI的基础知识 和JVMTI和JDWP不一样的是,JDI提供了一组接口,这些接口是纯JAVA编写的.他们主要是给开发环境IDE用的,虽然调试器的实现可以直接利用JDWP或者JVMTI,但是多了这一层则可以从用户的代码级别来定义要发送的请求和获取的信息. 本质上,Eclipse的调试器与目标VM之间的双向通信如下: 调试器将用户的操作转化为调试命令,命令通过链接被发送到前端运行目标程序

JPDA 架构研究19 - JDI的连接模块

引入: 上文提到了JDI的Mirror机制,把整个目标虚拟机上的所有数据.类型.域.方法.事件.状态和资源,以及调试器发向目标虚拟机的事件请求等都映射成Mirror 对象.这里进一步讨论JDI的链接模块. 分析: 连接模块其主要目的是提供调试器(Debugger)到目标虚拟机(Target VM)之间的交互通道. 从连接的发起方来看:连接的发起方可以是调试器,也可以是目标虚拟机. 从连接的数量来看,一个调试器可以连接多个目标VM, 但是一个目标VM只可以连接一个调试器. 我们从调试器(Debug

JPDA 架构研究18 - JDI的Mirror机制

引入: 上篇文章从整体上来看待JDI协议,这里结合Eclipse的实现代码来讨论下JDI的Mirror机制. 分析: 镜像机制是将目标虚拟机上的所有数据.类型.域.方法.事件.状态和资源,以及调试器发向目标虚拟机的事件请求等都映射成 Mirror 对象.其思想和我们经常说的O/R Mapping一样,其主要思想就是把2个异构的系统中的事物统一起来. Mirror接口是JDI规范中定义的主接口,它位于com.sun.jdi包中: public abstract interface Mirror {

JPDA 架构研究5 - Agent利用环境指针访问VM (内存管理篇)

引入: 我们在前面说到JVMTI的客户端Agent,又提到Agent通过环境指针来访问VM.这里就来看看环境指针到底有多大的访问VM的能力. 分类1:内存管理 a.Allocate. 分配内存 jvmtiError Allocate(jvmtiEnv* env,             jlong size,             unsigned char** mem_ptr) size:分配的字节数. mem_ptr:分配内存的首地址. b.Deallocate.释放内存,释放已经分配的内

JPDA 架构研究6 - Agent利用环境指针访问VM (线程管理篇)

引入: 上篇文章讲解了分类:内存管理,现在讲线程类操作的接口. 分类2:线程类操作 a.GetThreadState.获取线程状态 jvmtiError GetThreadState(jvmtiEnv* env,             jthread thread,             jint* thread_state_ptr) 大家都知道线程有很多种状态,比如Alive,Terminated,Runnable, 等待进入Synchronize Block,Waiting,Sleepi

JPDA 架构研究7 - Agent利用环境指针访问VM(线程组管理篇)

引入: 上篇文章中我们讨论了Agent利用环境指针访问VM的线程操作,这里讨论线程组操作. 分类3:线程组操作 a.GetTopThreadGroups.让Agent获取VM中的所有全局的线程组. jvmtiError GetTopThreadGroups(jvmtiEnv* env,             jint* group_count_ptr,             jthreadGroup** groups_ptr) 函数会返回全局的线程组的数量和线程组的列表. b.GetThre