[转帖]Java 平台调试体系
https://www.cnblogs.com/xiaojiesir/p/15652619.html
Java 平台调试体系(Java Platform Debugger Architecture,JPDA),由三个相对独立的层次共同组成。这三个层次由低到高分别是 Java 虚拟机工具接口(JVMTI)、Java 调试连接协议(JDWP)以及 Java 调试接口(JDI)。
| 模块 | 层次 | 编程语言 | 作用 |
|---|---|---|---|
| JVMTI | 底层 | C | 获取及控制当前虚拟机状态 |
| JDWP | 中间层 | C | 定义 JVMTI 和 JDI 交互的数据格式 |
| JDI | 高层 | Java | 提供 Java API 来远程控制被调试虚拟机 |
服务端 JVM 配置
如何在 JVM 中启用 JDWP,以供远程调试。 假设主启动类是 com.xxx.Test。
在 Windows 机器上:
java -Xdebug -Xrunjdwp:transport=dt_shmem,address=debug,server=y,suspend=y com.xxx.Test
在 Solaris 或 Linux 操作系统上:
java -Xdebug -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=y com.xxx.Test
-Xdebug:通知JVM工作在debug模式下;
-Xrunjdwp:通知JVM使用(java debug wire protocol)来运行调试环境;
transport:传输方式,Linux 采用监听Socket端口连接方式(也可以采用 dt_shmem共享内存方式,只限于windows机器,并且服务提供端和调试端只能位于同一台机);
server:server=y表示当前是调试服务端,=n表示当前是调试客户端;
suspend=y 会让 Java 进程启动时先挂起,等到有调试器连接上以后继续执行程序,一般用于被动连接;suspend=n 的话,则此 Java 进程会直接执行,但是我们可以随时通过调试器连上进程,一般用于设置主动连接。
就是说,比如说我们启动一个 Web 服务器进程,当这个值是 y 的时候,服务器的 JVM 初始化以后不会启动 Web 服务器,会一直等到我们用 IDEA 或 Eclipse、JDB 等工具连上这个 Java 进程后,再继续启动 Web 服务器。而如果是 n 的话,则会不管有没有调试器连接,都会正常运行。
通过这些启动参数,Test 类将运行在调试模式下,并等待调试器连接到 JVM 的调试地址:在 Windows 上是 Debug,在 Oracle Solaris 或 Linux 操作系统上是 8888 端口。
IDEA 中 Debug 模式启动的程序,自动设置了类似的启动选项。
JDB
启用了 JDWP 之后,可以使用各种客户端来进行调试/远程调试。比如 JDB 调试本地 JVM:
jdb -attach 'debug'
jdb -attach 8888
当 JDB 初始化并连接到 Test 之后,就可以进行 Java 代码级(Java-level)的调试。
但是 JDB 调试非常麻烦,比如说几个常用命令:
\1. 设置断点:
stop at 类名:行号
\2. 清除断点:
clear at 类名:行号
\3. 显示局部变量:
localx
\4. 显示变量 a 的值:
print a
\5. 显示当前线程堆栈:
wherei
\6. 代码执行到下一行:
next
\7. 代码继续执行,直到遇到下一个断点:
cont
可以看到使用 JDB 调试的话非常麻烦,所以我们一般还是在开发工具 IDE(IDEA、Eclipse)里调试代码。
开发工具 IDEA 中使用远程调试
远程调试
主动连接调试:服务端配置监控端口,本地IDE连接远程监听端口进行调试,一般调试问题用这种方式。
被动连接调试:本地IDE监听某端口,等待远程连接本地端口。一般用于远程服务启动不了,启动时连接到本地调试分析。
下面介绍 IDEA 中怎样使用远程调试。与常规的 Debug 配置类似,进入编辑:

添加 Remote(不是 Tomcat 下面的那个 Remote Server):

选择mode

调试模式:默认为Attach ;
Attach :此种模式下,调试服务端(被调试远程运行的机器)启动一个端口等待我们(调试客户端)去连接;
listen :此种模式下,是我们(调试客户端)去监听一个端口,当调试服务端准备好了,就会进行连接。
选择transport

传输方式,默认为Socket ;
套接字:MACOS,Linux的系统使用此种传输方式;
共享内存:WINDOWS系统使用此种传输方式。
然后配置端口号,比如 8888。

然后点击应用(Apply)按钮。
点击 Debug 的那个按钮即可启动远程调试,连上之后就和调试本地程序一样了。当然,记得加断点或者条件断点。
注意:远程调试时,需要保证服务端 JVM 中运行的代码和本地完全一致,否则可能会有莫名其妙的问题。
细心的同学可能已经发现,IDEA 给出了远程 JVM 的启动参数,建议使用 agentlib 的方式:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8888
远程调试代码不仅在开发程序的过程中非常有用,而且实际生产环境,有时候我们无法判断程序运行的过程中出现了什么问题,到时运行结果跟期望值不一致,这时候就可以使用远程调试功能连接到生产环境,从而可以追踪导致执行过程中的哪个步骤出了问题。
JVM 为什么可以让不同的开发工具和调试器都连接上进行调试呢?因为它提供了一套公开的调试信息的交互协议,各家厂商就可以根据这个协议去实现自己的调试图形工具,进而方便 Java 开发人员的使用。下面就简单谈谈这个协议。
JDWP 协议规范
JDWP 全称是 Java Debug Wire Protocol,中文翻译为“Java 调试连接协议”,是用于规范调试器(Debugger)与目标 JVM 之间通信的协议。
JDWP 是一个可选组件,可能在某些 JDK 实现中不可用。
JDWP 支持两种调试场景:
- 同一台计算机上的其他进程
- 远程计算机上
与许多协议规范的不同之处在于,JDWP 只规定了具体的格式和布局,而不管你用什么协议来传输数据。
JDWP 实现可以只使用简单的 API 来接受不同的传输机制。具体的传输不一定支持各种组合。
JDWP 设计得非常简洁,容易实现,而且对于未来的升级也足够灵活。
当前,JDWP 没有指定任何传输机制。将来如果发生变更,会在单独的文档中来进行规范。
JDWP 是 JPDA 中的一层。JPDA(Java Platform Debugger Architecture,Java 平台调试器体系结构)架构还包含更上层的 Java 调试接口(JDI,Java Debug Interface)。JDWP 旨在促进 JDI 的有效使用;为此,它的许多功能都是量身定制的。
对于那些用 Java 语言编写的 Debugger 工具来说,直接使用 JDI 比起 JDWP 更加方便。
JDWP 握手过程
连接建立之后,在发送其他数据包之前,连接双方需要进行握手:
握手过程包括以下步骤:
- Debugger 端向目标 JVM 发送 14 个字节,也就是包括 14 个 ASCII 字符的字符串 "JDWP-Handshake"。
- VM 端以相同的 14 个字节答复:JDWP-Handshake。
JDWP 数据包
JDWP 是无状态的协议,基于数据包来传输数据。包含两种基本的数据包类型:命令包(Command Packet)和应答包(Reply Packet)。
调试器和目标 VM 都可以发出命令包,调试器可以用命令包来从目标 VM 请求相关信息或者控制程序的执行,目标 VM 可以将自身的某些事件(例如断点或异常)用命令数据包的方式通知调试器。
应答包仅用于对命令包进行响应,并且标明该命令是成功还是失败。 应答包还可以携带命令中请求的数据(例如字段或变量的值)。当前,从目标 VM 发出的事件不需要调试器的应答。
JDWP 是异步的,在收到某个应答之前,可以发送多个命令包。
命令包和应答包的 header 大小相等。这样使传输更易于实现和抽象。每个数据包的布局如下所示。
命令包(Command Packet)
- Header
- length(4 bytes)
- id(4 bytes)
- flags(1 byte)
- command set(1 byte)
- command(1 byte)
- data(长度不固定)
应答包(Reply Packet)
- Header
- length(4 bytes)
- id(4 bytes)
- flags(1 byte)
- error code(2 bytes)
- data(Variable)
可以看到,这两种数据包的 Header 中,前三个字段格式是相同的。
通过 JDWP 发送的所有字段和数据都应采用大端字节序(big-endian)。大端字节序的定义请参考《Java 虚拟机规范》。
数据包字段说明
通用 Header 字段
下面的 Header 字段是命令包与应答包通用的。
length
length 字段表示整个数据包(包括 header)的字节数。因为数据包 header 的大小为 11 个字节,因此没有 data 的数据包会将此字段值设置为 11。
id
id 字段用于唯一标识每一对数据包(command/reply)。应答包 id 值必须与对应的命令包 ID 相同。这样异步方式的命令和应答就能匹配起来。同一个来源发送的所有未完成命令包的 id 字段必须唯一。(调试器发出的命令包,与 JVM 发出的命令包如果 ID 相同也没关系。) 除此之外,对 ID 的分配没有任何要求。对于大多数实现而言,使用自增计数器就足够了。id 的取值允许 2^32 个数据包,足以应对各种调试场景。
flags
flags 标志用于修改命令的排队和处理方式,也用来标记源自 JVM 的数据包。当前只定义了一个标志位 0x80,表示此数据包是应答包。协议的未来版本可能会定义其他标志。
命令包的 Header
除了前面的通用 Header 字段,命令包还有以下请求头。
command set
该字段主要用于通过一种有意义的方式对命令进行分组。Sun 定义的命令集,通过在 JDI 中支持的接口进行分组。例如,所有支持 VirtualMachine 接口的命令都在 VirtualMachine 命令集里面。命令集空间大致分为以下几类:
- 0-63:发给目标 VM 的命令集
- 64-127:发送给调试器的命令集
- 128-256:JVM 提供商自己定义的命令和扩展。
command
该字段用于标识命令集中的具体命令。该字段与命令集字段一起用于指示应如何处理命令包。更简洁地说,它们告诉接收者该怎么做。具体命令将在本文档后面介绍。
应答包的 Header
除了前面的通用 Header 字段,应答包还有以下请求头。
error code
此字段用于标识是否成功处理了对应的命令包。0 值表示成功,非零值表示错误。返回的错误代码由具体的命令集/命令规定,但是通常会映射为 JVM TI 标准错误码。
Data
每个命令的 Data 部分都是不同的。相应的命令包和应答包之间也有所不同。例如,请求命令包希望获取某个字段的值,可以在 Data 中填上 object ID 和 field ID。应答包的 Data 字段将存放该字段的值。
JDWP 中常用的数据类型
通常,命令或应答包的 Data 字段格式由具体的命令规定。Data 中的每个字段都是(Java 标准的)大端格式编码。下面介绍每个 Data 字段的数据类型。
大部分 JDWP 数据包中的数据类型如下所述。
| Name | Size |
|---|---|
| byte | 1 byte |
| boolean | 1 byte |
| int | 4 bytes |
| long | 8 bytes |
| objectID | 由具体的 JVM 确定,最多 8 字节 |
| tagged-objectID | objectID 的大小 +1 字节 |
| threadID | 同 objectID |
| threadGroupID | 同 objectID |
| stringID | 同 objectID |
| classLoaderID | 同 objectID |
| classObjectID | 同 objectID |
| arrayID | 同 objectID |
| referenceTypeID | 同 objectID |
| classID | 同 referenceTypeID |
| interfaceID | 同 referenceTypeID |
| arrayTypeID | 同 referenceTypeID |
| methodID | 由具体的 JVM 确定,最多 8 字节 |
| fieldID | 由具体的 JVM 确定,最多 8 字节 |
| frameID | 由具体的 JVM 确定,最多 8 字节 |
| location | 由具体的 JVM 确定 |
| string | 长度不固定 |
| value | 长度不固定 |
| untagged-value | 长度不固定 |
| arrayregion | 长度不固定 |
不同的 JVM 中,Object IDs、Reference Type IDs、Field IDs、Method IDs 和 Frame IDs 的大小可能不同。
通常,它们的大小与 JNI 和 JVMDI 调用中用于这些项目的 native 标识符的大小相对应。这些类型中最大的 size 为 8 个字节。当然,调试器可以使用 "idSizes" 这个命令来确定每种类型的大小。
如果 JVM 收到的命令包里面含有未实现(non-implemented)或无法识别(non-recognized)的命令/命令集,则会返回带有错误码 NOT_IMPLEMENTED 的应答包。
[转帖]Java 平台调试体系的更多相关文章
- 深入 Java 调试体系: 第 1 部分,JPDA 体系概览
JPDA 概述 所有的程序员都会遇到 bug,对于运行态的错误,我们往往需要一些方法来观察和测试运行态中的环境.在 Java 程序中,最简单的,您是否尝试过使用 System.out.println( ...
- 深入 Java 调试体系: 第 1 部分,初探JPDA 体系
JPDA(Java Platform Debugger Architecture)是 Java 平台调试体系结构的缩写,通过 JPDA 提供的 API,开发人员可以方便灵活的搭建 Java 调试应用程 ...
- Atitit.获取swing ui 按钮控件的id 与名字 与JPDA 调试体系
Atitit.获取swing ui 按钮控件的id 与名字 与JPDA 调试体系 1. Swing Inspector是一个Java Swing/AWT用户界面分析和调试工具,功能与firebug类似 ...
- 使用java远程调试技术监控代码运行
JAPA介绍 JPDA(Java Platform Debugger Architecture)是 Java 平台调试体系结构的缩写,通过 JPDA 提供的 API,开发人员可以方便灵活的搭建 Jav ...
- linux下的java远程调试jpda+tomcat
项目放到linux服务器了,服务器的环境或者数据可能和我们本地不一样,这个时候我们可能需要远程的断点进行调试,来查看请求过程中的各个变量的值.这里我们的应用服务器用的tomcat5.5.17 这个时候 ...
- [置顶] 遵循Java EE标准体系的开源GIS服务平台之二:平台部署
传送门 ☞ 系统架构设计 ☞ 转载请注明 ☞ http://blog.csdn.net/leverage_1229 传送门 ☞ GoF23种设计模式 ☞ 转载请注明 ☞ http://blog.csd ...
- [置顶] 遵循Java EE标准体系的开源GIS服务平台架构
传送门 ☞ 系统架构设计 ☞ 转载请注明 ☞ http://blog.csdn.net/leverage_1229 传送门 ☞ GoF23种设计模式 ☞ 转载请注明 ☞ http://blog.csd ...
- Java开发中的高频Collections用法总结与Java平台实现源代码查看方式
一生二,二生三,三生万物,基础永远是一个计算机人的立身之本,相信看到这篇文章的人一般都知道数据结构这门课程,要不也不会找到我的这篇文章.数据结构这门课程的分析奠定了工程师对各种平台中的容器类,集合类的 ...
- .NET平台技术体系梳理+初学者学习路径推荐+我们的愿景与目标
文章出自:http://www.cnblogs.com/ice-river/p/3475041.html 一 .NET平台技术体系梳理 .NET平台应用领域众多(桌面开发,web开发,移动开发),不断 ...
- .NET平台技术体系
.NET平台技术体系梳理+初学者学习路径推荐+我们的愿景与目标 一 .NET平台技术体系梳理 .NET平台应用领域众多(桌面开发,web开发,移动开发),不断有新的编程爱好者开始学习或从其他平台转移到 ...
随机推荐
- CPU高速缓存与极性代码设计
摘要:CPU内置少量的高速缓存的重要性不言而喻,在体积.成本.效率等因素下产生了当今用到的计算机的存储结构. 介绍 cpu缓存的结构 缓存的存取与一致 代码设计的考量 最后 CPU频率太快,其处理速度 ...
- 云小课 | 一分钟了解AppCube中的应用
阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:应用魔方(AppC ...
- Gartner 权威解读: SBOM 采用率将于2025年达到60%
随着现代软件开发越来越依赖于第三方资源,针对软件供应链的恶意攻击数量也随之激增.据业内权威机构 Gartner 预计,软件物料清单 (SBOM) 的采用率在 2025 年将会达到 60%. Gartn ...
- MongoDB 读写分离——Windows MongoDB 副本集配置
本文基于:MongoDB 读写分离--MongoDB 安装 参考:https://docs.mongodb.com/manual/tutorial/deploy-replica-set-for-tes ...
- 深入了解 ReadDirectoryChangesW 并应用其监控文件目录
简介 监视指定目录的更改,并将有关更改的信息打印到控制台,该功能的实现不仅可以在内核层,在应用层同样可以.程序中使用 ReadDirectoryChangesW 函数来监视目录中的更改,并使用 FIL ...
- 对于 CDN 的多元理解
这是二狗子为数不多的创业故事. 那时二狗子还是一名高中生,学校是封闭式管理.由于二狗子总忍不住上课吃零食,他便每周一都会背着一麻袋零食来上学. 这上课吃零食行为,不知不觉诱惑到了周围的同学.大家纷纷向 ...
- 3.1 《数据库系统概论》SQL概述及数据定义(模式SCHEMA、表TABLE、索引INDEX)
前言 本篇文章学习书籍:<数据库系统概论>第5版 王珊 萨师煊编著 视频资源来自:数据库系统概论完整版(基础篇+高级篇+新技术篇) 由于 BitHachi 学长已经系统的整理过本书了,我在 ...
- 三、mycat实验数据
系列导航 一.Mycat实战---为什么要用mycat 二.Mycat安装 三.mycat实验数据 四.mycat垂直分库 五.mycat水平分库 六.mycat全局自增 七.mycat-ER分片 最 ...
- element-ui实现部分引用
1.首先安装 babel-plugin-component组件: 2.修改babel.js配置 1 module.exports = { 2 presets: [ 3 '@vue/app', 4 [' ...
- springboot2.0+dubbo-spring-boot-starter聚合项目打可执行的jar包
springboot2.0+dubbo聚合项目打可执行的jar包 springboot2.0+dubbo-spring-boot-starter项目服务方打包和以前老版本的dubbo打包方式不一样,不 ...