当前位置:

首页 > 编程开发 > Java平台调试体系的基本原理是什么?

Java平台调试体系的基本原理是什么?

一、原理分析(一)介绍JPDA(JavaPlatformDebuggerArchitecture)是Java平台调试体系结构的缩写,通过JPDA提供的API,开发人员可以方便灵活的搭建Java调试应用程序。JPDA主要由三个部分组成:Java虚拟机工具接口(JVMTI),Java调试线协议(JDWP),以及Java调试接口(JDI)。Java程序都是运行在Java虚拟机上的,我们要调试Java程序,事实上就需要向Java虚拟机请求当前运行态的状态,并对虚拟机发出一定的指令,设置一些回调等等,那么Java的

    一、原理分析

    (一)介绍

    JPDA(Java Platform Debugger Architecture) 是 Java 平台调试体系结构的缩写,通过 JPDA 提供的 API,开发人员可以方便灵活的搭建 Java 调试应用程序。

    JPDA 主要由三个部分组成:Java 虚拟机工具接口(JVMTI),Java 调试线协议(JDWP),以及 Java 调试接口(JDI)。

    Java 程序都是运行在 Java 虚拟机上的,我们要调试 Java 程序,事实上就需要向 Java 虚拟机请求当前运行态的状态,并对虚拟机发出一定的指令,设置一些回调等等,那么 Java 的调试体系,就是虚拟机的一整套用于调试的工具和接口。

    (二)IDEA和eclipse 调试原理为

    1:编辑器作为客户端和服务器程序通过暴露的监听端口建立socket连接

    2:IDE客户端将断点位置创建了断点事件通过 JDI 接口传给了 服务端(程序端)的 VM,VM 调用 suspend 将 VM 挂起

    3:VM 挂起之后将客户端需要获取的 VM 信息返回给客户端,返回之后 VM resume 恢复其运行状态

    4:客户端获取到 VM 返回的信息之后可以通过不同的方式进行展示

    (三)架构体系

    JPDA 定义了一个完整独立的体系,它由三个相对独立的层次共同组成,而且规定了它们三者之间的交互方式,或者说定义了它们通信的接口。

    这三个层次由低到高分别是 Java 虚拟机工具接口(JVMTI),Java 调试线协议(JDWP)以及 Java 调试接口(JDI)。

    这三个模块把调试过程分解成几个很自然的概念:调试者(debugger)和被调试者(debuggee),以及他们中间的通信器。

    被调试者运行于我们想调试的 Java 虚拟机之上,它可以通过 JVMTI 这个标准接口,监控当前虚拟机的信息;调试者定义了用户可使用的调试接口,通过这些接口,用户可以对被调试虚拟机发送调试命令,同时调试者接受并显示调试结果。

    在调试者和被调试着之间,调试命令和调试结果,都是通过 JDWP 的通讯协议传输的。所有的命令被封装成 JDWP 命令包,通过传输层发送给被调试者,被调试者接收到 JDWP 命令包后,解析这个命令并转化为 JVMTI 的调用,在被调试者上运行。

    类似的,JVMTI 的运行结果,被格式化成 JDWP 数据包,发送给调试者并返回给 JDI 调用。而调试器开发人员就是通过 JDI 得到数据,发出指令。

    Java平台调试体系的基本原理是什么?

    如上图所示JPDA 由三层组成:

    • JVM TI - Java VM 工具接口。定义 VM 提供的调试服务。

    • JDWP - Java 调试通信协议。定义被调试者和调试器进程之间的通信。

    • JDI - Java 调试接口。定义一个高级 Java 语言接口,工具开发人员可以轻松地使用它来编写远程调试器应用程序。

    通过 JPDA 这套接口,我们就可以开发自己的调试工具。通过这些 JPDA 提供的接口和协议,调试器开发人员就能根据特定开发者的需求,扩展定制 Java 调试应用程序。

    前面我们提到的 IDE 调试工具都是基于 JPDA 体系开发的,区别仅仅在于它们可能提供了不同的图形界面、具有一些不同的自定义功能。

    另外,我们要注意的是,JPDA 是一套标准,任何的 JDK 实现都必须完成这个标准,因此,通过 JPDA 开发出来的调试工具先天具有跨平台、不依赖虚拟机实现、JDK 版本无关等移植优点,因此大部分的调试工具都是基于这个体系的。

    二、远程调试实例

    【1】构建一个SpringBoot的WEB项目。当前所选择的SpringBoot版本是2.3.0.RELEASE。对应的tomcat版本是9.X。

    【2】打包该SpringBoot项目,开发应用程序端口为9999。将该程序部署到Linux服务器上,可以是JAR包方式也可以Docker的方式,远程调试和这个没有关系。

    【3】部署程序的代码参考如下,就是一个简单的请求处理打印输出信息

    /** 
     *   测试程序
     * @author zhangyu
     * @date 2022/2/17
    */
    @SpringBootApplication
    @RestController
    public class DebuggerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DebuggerApplication.class, args);
        }
    
        @GetMapping("/test")
        public  String test(){
            System.out.println(111);
            System.out.println(222);
            return "OK";
        }
    
    }

    【4】部署程序启动参数如下

    java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8888 -jar debugger-0.0.1-SNAPSHOT.jar

    其中address=8888表示开启8888端口作为远程调试的Socket通信端口

    Java平台调试体系的基本原理是什么?

    如果是部署在tomcat下的普通web项目,参考如下:

    小于 tomcat9 版本

    tomcat 中 bin/catalina.sh 中增加 CATALINA_OPTS=‘-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=18006’

    如下图所示:

    Java平台调试体系的基本原理是什么?

    大于等于 tomcat9 版本

    tomcat 中 bin/catalina.sh 中的 JPDA_ADDRESS=“localhost:8000” 这一句中的localhost修改为0.0.0.0(允许所有ip连接到8000端口,而不仅是本地)8000是端口,端口号可以任意修改成没有占用的即可

    如下图所示:

    Java平台调试体系的基本原理是什么?

    【5】测试部署的程序正常后,下面构建客户端远程调试,当前以IDEA工具作为客户端

    Java平台调试体系的基本原理是什么?

    参考:

    【1】-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8888

    【2】Host:远程服务器地址

    【3】Port:远程服务器开放的调试通信端口,非应用端口

    Java平台调试体系的基本原理是什么?

    Java平台调试体系的基本原理是什么?

    测试接口:http://XXX:9999/test。注意本地代码需要和远程部署程序一致。

    通过上图可以看到客户端设置断点已经生效,其中在客户端执行了一个调试输出,这个是自定义输出的内容服务器程序并没有,在执行后右侧的服务器控制台日志输出了该信息,因此远程Debug是正常通信和处理的。

    (一)调试参数详解

    • -Xdebug:启用调试特性

    • -Xrunjdwp:在目标 VM 中加载 JDWP 实现。它通过传输和 JDWP 协议与独立的调试器应用程序通信。下面介绍一些特定的子选项

    从 Java V5 开始,您可以使用 -agentlib:jdwp 选项,而不是 -Xdebug 和 -Xrunjdwp。但如果连接到 V5 以前的 VM,只能选择 -Xdebug 和 -Xrunjdwp。下面简单描述 -Xrunjdwp 子选项。

    • -Djava.compiler=NONE: 禁止 JIT 编译器的加载

    • transport : 传输方式,有 socket 和 shared memory 两种,我们通常使用 socket(套接字)传输,但是在 Windows 平台上也可以使用shared memory(共享内存)传输。

    • server(y/n): VM 是否需要作为调试服务器执行

    • address: 调试服务器的端口号,客户端用来连接服务器的端口号

    • suspend(y/n):值是 y 或者 n,若为 y,启动时候自己程序的 VM 将会暂停(挂起),直到客户端进行连接,若为 n,自己程序的 VM 不会挂起

    三、JDI工具代码实践

    (一)JDI技术架构

    Java平台调试体系的基本原理是什么?

    (二)实践案例

    (1)被调试程序

    创建一个SpringBoot的WEB项目,提供一个简单的测试接口,并在测试方法中提供一些方法参数变量和局部变量作为后面的调试测试用。

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    @RestController
    public class DebuggerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DebuggerApplication.class, args);
        }
    
        @GetMapping("/test")
        public  String test(String name){
            System.out.println("进入方法");
            int var=100;
            System.out.println(name);
            System.out.println(var);
            System.out.println("方法结束");
            return "OK";
        }
    }

    项目启动配置参考,需要启用Debug配置

    Java平台调试体系的基本原理是什么?

    (2)自定义调试器代码

    开发调试器需要JNI工具支持,JDI操作的API工具在tools.jar中 ,需要在 CLASSPATH 中添加/lib/tools.jar

    Java平台调试体系的基本原理是什么?

    import com.sun.jdi.*;
    import com.sun.jdi.connect.AttachingConnector;
    import com.sun.jdi.connect.Connector;
    import com.sun.jdi.event.*;
    import com.sun.jdi.request.BreakpointRequest;
    import com.sun.jdi.request.EventRequest;
    import com.sun.jdi.request.EventRequestManager;
    import com.sun.tools.jdi.SocketAttachingConnector;
    
    import java.util.List;
    import java.util.Map;
    
    /**
     *  通过JNI工具测试Debug
     * @author zhangyu
     * @date 2022/2/20
    */
    public class TestDebugVirtualMachine {
    
    
        private static VirtualMachine vm;
    
        public static void main(String[] args) throws Exception {
            //获取SocketAttachingConnector,连接其它JVM称之为附加(attach)操作
            VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
            List connectors = vmm.attachingConnectors();
            SocketAttachingConnector sac = null;
            for(AttachingConnector ac : connectors) {
                if(ac instanceof SocketAttachingConnector) {
                    sac = (SocketAttachingConnector) ac;
                }
            }
            assert sac != null;
            //设置好主机地址,端口信息
            Map arguments = sac.defaultArguments();
            Connector.Argument hostArg = arguments.get("hostname");
            Connector.Argument portArg = arguments.get("port");
            hostArg.setValue("127.0.0.1");
            portArg.setValue(String.valueOf(8800));
            //进行连接
             vm = sac.attach(arguments);
            //相应的请求调用通过requestManager来完成
            EventRequestManager eventRequestManager = vm.eventRequestManager();
            //创建一个代码判断,因此需要获取相应的类,以及具体的断点位置,即相应的代码行。
            ClassType clazz = (ClassType) vm.classesByName("com.zy.debugger.DebuggerApplication").get(0);
            //设置断点代码位置
            Location location = clazz.locationsOfLine(22).get(0);
            //创建新断点并设置阻塞模式为线程阻塞,即只有当前线程被阻塞
            BreakpointRequest breakpointRequest = eventRequestManager.createBreakpointRequest(location);
            //设置阻塞并启动
            breakpointRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
            breakpointRequest.enable();
            //获取vm的事件队列
            EventQueue eventQueue = vm.eventQueue();
            while(true) {
                //不断地读取事件并处理断点记录事件
                EventSet eventSet = eventQueue.remove();
                EventIterator eventIterator = eventSet.eventIterator();
                while(eventIterator.hasNext()) {
                    Event event = eventIterator.next();
                    execute(event);
                }
                //将相应线程resume,表示继续运行
                eventSet.resume();
            }
        }
    
        /**
         *  处理监听到事件
         * @author zhangyu
         * @date 2022/2/20
        */
        public static void execute(Event event) throws Exception {
            //获取的event为一个抽象的事件记录,可以通过类型判断转型为具体的事件,这里我们转型为BreakpointEvent,即断点记录,
            BreakpointEvent breakpointEvent = (BreakpointEvent) event;
            //并通过断点处的线程拿到线程帧,进而获取相应的变量信息,并打印记录。
            ThreadReference threadReference = breakpointEvent.thread();
            StackFrame stackFrame = threadReference.frame(0);
            List localVariables = stackFrame.visibleVariables();
            //输出当前线程栈帧保存的变量数据
            localVariables.forEach(t -> {
                Value value = stackFrame.getValue(t);
                System.out.println("local->" + value.type() + "," + value.getClass() + "," + value);
            });
        }
    
    }

    (3)代码分析

    【1】通过Bootstrap.virtualMachineManager();获取连接器,客户端即通过相应的connector进行连接,配置服务器程序ip地址和端口,连接后获取对应服务器的VM信息。

    【2】通过VirtualMachine获取类信息,通过遍历获取的类集合定位到目标debug的类文件上

    【3】对目标类代码特定位置设置并启用断点

    【4】记录断点信息,阻塞服务器线程,并根据对应事件获取相应的信息

    【5】执行event.resume释放断点,服务器程序继续运行

    (4)运行测试

    【1】启动服务器程序,即上面的SpringBoot的web项目。本地以debug方式启动调试器代码,待会在这个位置看看获取的信息,同时避免直接释放了断点。

    Java平台调试体系的基本原理是什么?

    【2】设置断点位置为DebuggerApplication类的第22行

    Java平台调试体系的基本原理是什么?

    【3】启动后测试该接口,可以发现服务器程序控制台打印了如下结果。第22行还没有执行。

    Java平台调试体系的基本原理是什么?

    【4】此时,在观察调试器程序。可以看到获取到了服务器程序栈帧的数据

    Java平台调试体系的基本原理是什么?

    【5】释放断点,服务器正常运行完本次请求处理流程

    Java平台调试体系的基本原理是什么?

    本文内容来源于互联网,如有侵权请联系删除。
    作者最新文章
    编程开发
    相关文章 更多
    C++动态数组初始化怎么写?常用语句与代码示例
    C++动态数组初始化怎么写?常用语句与代码示例

    深入解析C++中动态数组的初始化机制,涵盖new操作符的不同用法、基本类型与类对象的初始化差异,以及为何在现代C++开发中应优先使用std::vector。

    谷歌浏览器Mac版入口
    谷歌浏览器Mac版入口

    谷歌浏览器Mac版官方安装指南 谷歌浏览器Mac版官方安装入口是https://www.google.com/chrome/,需macOS 12+系统、500MB空间,下载.dmg后拖入应用程序安装,支持多设备同步、性能优化与隐私保护功能。 苹果电脑Chrome的安装入口究竟在哪里?这个问题最近可是

    Chrome浏览器JS脚本不运行怎么办
    Chrome浏览器JS脚本不运行怎么办

    Chrome中JavaScript未执行需依次检查:一、移除站点级禁用并添加允许域名;二、开启全局JavaScript开关;三、禁用干扰扩展;四、在开发者工具中启用JavaScript;五、重置内容设置为默认。 有时在Chrome里打开网页,会发现交互按钮点了没反应,数据加载不出来,页面仿佛“静止”

    IE浏览器怀旧版在线网址
    IE浏览器怀旧版在线网址

    IE浏览器怀旧版在线网址:一次精准的技术时光回溯 最近,不少老用户和怀旧爱好者在反复搜索一个问题:那个经典的Internet Explorer,如今还能在哪里原汁原味地体验到?答案指向一个特定的地址:https://ie.microsoft.com/legacy/。 这个网站远不止是一个简单的“皮肤

    火狐浏览器有哪些设置功能
    火狐浏览器有哪些设置功能

    火狐浏览器五大核心设置功能:解锁高效、安全与个性化体验 火狐浏览器功能强大,但如果不仔细挖掘,很多能大幅提升效率和安全性的设置可能就“藏着掖着”了。这就好比拥有一台高性能设备,却只用了基础模式。那么,如何把它调整到最顺手、最安全的状态?接下来,我们就聚焦于当前版本(截至2025年末)最关键的五大设置

    chrome搜索免验证入口
    chrome搜索免验证入口

    Chrome官方免验证入口为https://www.google.cn/chrome/,提供全平台安装包、免登录即用、本地化安全机制及引擎级性能优化。 到底该去哪里找正版、免费且无需繁琐验证的Chrome浏览器入口?这个问题困扰了不少网友。今天,我们就来直通核心,为大家详细拆解Chrome引擎的官方

    java heap space 选型思路:使用场景与区别整理
    java heap space 选型思路:使用场景与区别整理

    Java堆是JVM存储对象的核心内存区域,配置需结合场景:单体应用适中设置;大数据处理需大堆并关注GC停顿;微服务强调快速启动;高并发需精细划分堆区域。关键参数-Xms和-Xmx建议等值以稳定性能。垃圾回收器选择影响效率,如G1适用于大堆,ZGC可实现低停顿。内存错误时需监控堆状态。

    java heap space 使用中遇到的问题怎么解决
    java heap space 使用中遇到的问题怎么解决

    Java堆内存溢出错误通常因内存泄漏、数据处理需求过大或JVM参数配置不当引起。排查时可借助jmap、堆转储及MAT等工具定位问题。解决方案包括调整JVM内存参数(如-Xmx)、修复代码中的内存泄漏、优化大数据处理逻辑,并建立持续监控与预防机制,以保障应用稳定运行。

    java xml 选型思路:使用场景与区别整理
    java xml 选型思路:使用场景与区别整理

    XML在Java开发中用于配置、数据交换等场景。解析方式主要有DOM、SAX、StAX及第三方库。DOM适合操作小文件,SAX/StAX适合处理大文件流,JAXB用于对象与XML映射。选型需结合数据大小、内存、性能及团队熟悉度,现代框架常封装底层解析。

    using namespace 使用中遇到的问题怎么解决
    using namespace 使用中遇到的问题怎么解决

    命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件
    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    Mac软件 更多
    灵活计算器
    灵活计算器
    macOS/iOS/Android

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    赤友清理大师
    赤友清理大师
    macOS

    赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    极度公式
    极度公式
    Windows/macOS/Linux

    极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    WINDOWS 更多
    Windows 10
    Windows 10
    Windows

    Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

    极度公式
    极度公式
    Windows/macOS/Linux

    极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    密码键盘
    密码键盘
    Windows/macOS/iOS/Android

    密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。