欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java平臺調(diào)試體系原理分析和實(shí)踐整理 遠(yuǎn)程Debug

 更新時(shí)間:2023年03月09日 14:35:58   作者:ZWZhangYu  
這篇文章主要介紹了Java平臺調(diào)試體系原理分析和實(shí)踐整理 遠(yuǎn)程Debug,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

一、原理分析

(一)介紹

JPDA(Java Platform Debugger Architecture) 是 Java 平臺調(diào)試體系結(jié)構(gòu)的縮寫,通過 JPDA 提供的 API,開發(fā)人員可以方便靈活的搭建 Java 調(diào)試應(yīng)用程序。

JPDA 主要由三個(gè)部分組成:Java 虛擬機(jī)工具接口(JVMTI),Java 調(diào)試線協(xié)議(JDWP),以及 Java 調(diào)試接口(JDI)。

Java 程序都是運(yùn)行在 Java 虛擬機(jī)上的,我們要調(diào)試 Java 程序,事實(shí)上就需要向 Java 虛擬機(jī)請求當(dāng)前運(yùn)行態(tài)的狀態(tài),并對虛擬機(jī)發(fā)出一定的指令,設(shè)置一些回調(diào)等等,那么 Java 的調(diào)試體系,就是虛擬機(jī)的一整套用于調(diào)試的工具和接口。

(二)IDEA和eclipse 調(diào)試原理為

1:編輯器作為客戶端和服務(wù)器程序通過暴露的監(jiān)聽端口建立socket連接

2:IDE客戶端將斷點(diǎn)位置創(chuàng)建了斷點(diǎn)事件通過 JDI 接口傳給了 服務(wù)端(程序端)的 VM,VM 調(diào)用 suspend 將 VM 掛起

3:VM 掛起之后將客戶端需要獲取的 VM 信息返回給客戶端,返回之后 VM resume 恢復(fù)其運(yùn)行狀態(tài)

4:客戶端獲取到 VM 返回的信息之后可以通過不同的方式進(jìn)行展示

(三)架構(gòu)體系

JPDA 定義了一個(gè)完整獨(dú)立的體系,它由三個(gè)相對獨(dú)立的層次共同組成,而且規(guī)定了它們?nèi)咧g的交互方式,或者說定義了它們通信的接口。

這三個(gè)層次由低到高分別是 Java 虛擬機(jī)工具接口(JVMTI),Java 調(diào)試線協(xié)議(JDWP)以及 Java 調(diào)試接口(JDI)。

這三個(gè)模塊把調(diào)試過程分解成幾個(gè)很自然的概念:調(diào)試者(debugger)和被調(diào)試者(debuggee),以及他們中間的通信器。

被調(diào)試者運(yùn)行于我們想調(diào)試的 Java 虛擬機(jī)之上,它可以通過 JVMTI 這個(gè)標(biāo)準(zhǔn)接口,監(jiān)控當(dāng)前虛擬機(jī)的信息;調(diào)試者定義了用戶可使用的調(diào)試接口,通過這些接口,用戶可以對被調(diào)試虛擬機(jī)發(fā)送調(diào)試命令,同時(shí)調(diào)試者接受并顯示調(diào)試結(jié)果。

在調(diào)試者和被調(diào)試著之間,調(diào)試命令和調(diào)試結(jié)果,都是通過 JDWP 的通訊協(xié)議傳輸?shù)?。所有的命令被封裝成 JDWP 命令包,通過傳輸層發(fā)送給被調(diào)試者,被調(diào)試者接收到 JDWP 命令包后,解析這個(gè)命令并轉(zhuǎn)化為 JVMTI 的調(diào)用,在被調(diào)試者上運(yùn)行。

類似的,JVMTI 的運(yùn)行結(jié)果,被格式化成 JDWP 數(shù)據(jù)包,發(fā)送給調(diào)試者并返回給 JDI 調(diào)用。而調(diào)試器開發(fā)人員就是通過 JDI 得到數(shù)據(jù),發(fā)出指令。

如上圖所示JPDA 由三層組成:

  • JVM TI - Java VM 工具接口。定義 VM 提供的調(diào)試服務(wù)。
  • JDWP - Java 調(diào)試通信協(xié)議。定義被調(diào)試者和調(diào)試器進(jìn)程之間的通信。
  • JDI - Java 調(diào)試接口。定義一個(gè)高級 Java 語言接口,工具開發(fā)人員可以輕松地使用它來編寫遠(yuǎn)程調(diào)試器應(yīng)用程序。

通過 JPDA 這套接口,我們就可以開發(fā)自己的調(diào)試工具。通過這些 JPDA 提供的接口和協(xié)議,調(diào)試器開發(fā)人員就能根據(jù)特定開發(fā)者的需求,擴(kuò)展定制 Java 調(diào)試應(yīng)用程序。

前面我們提到的 IDE 調(diào)試工具都是基于 JPDA 體系開發(fā)的,區(qū)別僅僅在于它們可能提供了不同的圖形界面、具有一些不同的自定義功能。

另外,我們要注意的是,JPDA 是一套標(biāo)準(zhǔn),任何的 JDK 實(shí)現(xiàn)都必須完成這個(gè)標(biāo)準(zhǔn),因此,通過 JPDA 開發(fā)出來的調(diào)試工具先天具有跨平臺、不依賴虛擬機(jī)實(shí)現(xiàn)、JDK 版本無關(guān)等移植優(yōu)點(diǎn),因此大部分的調(diào)試工具都是基于這個(gè)體系的。

二、遠(yuǎn)程調(diào)試實(shí)例

【1】構(gòu)建一個(gè)SpringBoot的WEB項(xiàng)目。當(dāng)前所選擇的SpringBoot版本是2.3.0.RELEASE。對應(yīng)的tomcat版本是9.X。

【2】打包該SpringBoot項(xiàng)目,開發(fā)應(yīng)用程序端口為9999。將該程序部署到Linux服務(wù)器上,可以是JAR包方式也可以Docker的方式,遠(yuǎn)程調(diào)試和這個(gè)沒有關(guān)系。

【3】部署程序的代碼參考如下,就是一個(gè)簡單的請求處理打印輸出信息

/** 
 *   測試程序
 * @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】部署程序啟動參數(shù)如下

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

其中address=8888表示開啟8888端口作為遠(yuǎn)程調(diào)試的Socket通信端口

如果是部署在tomcat下的普通web項(xiàng)目,參考如下:

小于 tomcat9 版本

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

如下圖所示:

大于等于 tomcat9 版本

tomcat 中 bin/catalina.sh 中的 JPDA_ADDRESS=“localhost:8000” 這一句中的localhost修改為0.0.0.0(允許所有ip連接到8000端口,而不僅是本地)8000是端口,端口號可以任意修改成沒有占用的即可

如下圖所示:

【5】測試部署的程序正常后,下面構(gòu)建客戶端遠(yuǎn)程調(diào)試,當(dāng)前以IDEA工具作為客戶端

參考:

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

【2】Host:遠(yuǎn)程服務(wù)器地址

【3】Port:遠(yuǎn)程服務(wù)器開放的調(diào)試通信端口,非應(yīng)用端口

測試接口:http://XXX:9999/test。注意本地代碼需要和遠(yuǎn)程部署程序一致。

通過上圖可以看到客戶端設(shè)置斷點(diǎn)已經(jīng)生效,其中在客戶端執(zhí)行了一個(gè)調(diào)試輸出,這個(gè)是自定義輸出的內(nèi)容服務(wù)器程序并沒有,在執(zhí)行后右側(cè)的服務(wù)器控制臺日志輸出了該信息,因此遠(yuǎn)程Debug是正常通信和處理的。

(一)調(diào)試參數(shù)詳解

  • -Xdebug:啟用調(diào)試特性
  • -Xrunjdwp:在目標(biāo) VM 中加載 JDWP 實(shí)現(xiàn)。它通過傳輸和 JDWP 協(xié)議與獨(dú)立的調(diào)試器應(yīng)用程序通信。下面介紹一些特定的子選項(xiàng)

從 Java V5 開始,您可以使用 -agentlib:jdwp 選項(xiàng),而不是 -Xdebug 和 -Xrunjdwp。但如果連接到 V5 以前的 VM,只能選擇 -Xdebug 和 -Xrunjdwp。下面簡單描述 -Xrunjdwp 子選項(xiàng)。

  • -Djava.compiler=NONE: 禁止 JIT 編譯器的加載
  • transport : 傳輸方式,有 socket 和 shared memory 兩種,我們通常使用 socket(套接字)傳輸,但是在 Windows 平臺上也可以使用shared memory(共享內(nèi)存)傳輸。
  • server(y/n): VM 是否需要作為調(diào)試服務(wù)器執(zhí)行
  • address: 調(diào)試服務(wù)器的端口號,客戶端用來連接服務(wù)器的端口號
  • suspend(y/n):值是 y 或者 n,若為 y,啟動時(shí)候自己程序的 VM 將會暫停(掛起),直到客戶端進(jìn)行連接,若為 n,自己程序的 VM 不會掛起

三、JDI工具代碼實(shí)踐

(一)JDI技術(shù)架構(gòu)

(二)實(shí)踐案例

(1)被調(diào)試程序

創(chuàng)建一個(gè)SpringBoot的WEB項(xiàng)目,提供一個(gè)簡單的測試接口,并在測試方法中提供一些方法參數(shù)變量和局部變量作為后面的調(diào)試測試用。

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("進(jìn)入方法");
        int var=100;
        System.out.println(name);
        System.out.println(var);
        System.out.println("方法結(jié)束");
        return "OK";
    }
}

項(xiàng)目啟動配置參考,需要啟用Debug配置

(2)自定義調(diào)試器代碼

開發(fā)調(diào)試器需要JNI工具支持,JDI操作的API工具在tools.jar中 ,需要在 CLASSPATH 中添加/lib/tools.jar

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<AttachingConnector> connectors = vmm.attachingConnectors();
        SocketAttachingConnector sac = null;
        for(AttachingConnector ac : connectors) {
            if(ac instanceof SocketAttachingConnector) {
                sac = (SocketAttachingConnector) ac;
            }
        }
        assert sac != null;
        //設(shè)置好主機(jī)地址,端口信息
        Map<String, Connector.Argument> 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));
        //進(jìn)行連接
         vm = sac.attach(arguments);
        //相應(yīng)的請求調(diào)用通過requestManager來完成
        EventRequestManager eventRequestManager = vm.eventRequestManager();
        //創(chuàng)建一個(gè)代碼判斷,因此需要獲取相應(yīng)的類,以及具體的斷點(diǎn)位置,即相應(yīng)的代碼行。
        ClassType clazz = (ClassType) vm.classesByName("com.zy.debugger.DebuggerApplication").get(0);
        //設(shè)置斷點(diǎn)代碼位置
        Location location = clazz.locationsOfLine(22).get(0);
        //創(chuàng)建新斷點(diǎn)并設(shè)置阻塞模式為線程阻塞,即只有當(dāng)前線程被阻塞
        BreakpointRequest breakpointRequest = eventRequestManager.createBreakpointRequest(location);
        //設(shè)置阻塞并啟動
        breakpointRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
        breakpointRequest.enable();
        //獲取vm的事件隊(duì)列
        EventQueue eventQueue = vm.eventQueue();
        while(true) {
            //不斷地讀取事件并處理斷點(diǎn)記錄事件
            EventSet eventSet = eventQueue.remove();
            EventIterator eventIterator = eventSet.eventIterator();
            while(eventIterator.hasNext()) {
                Event event = eventIterator.next();
                execute(event);
            }
            //將相應(yīng)線程resume,表示繼續(xù)運(yùn)行
            eventSet.resume();
        }
    }

    /**
     *  處理監(jiān)聽到事件
     * @author zhangyu
     * @date 2022/2/20
    */
    public static void execute(Event event) throws Exception {
        //獲取的event為一個(gè)抽象的事件記錄,可以通過類型判斷轉(zhuǎn)型為具體的事件,這里我們轉(zhuǎn)型為BreakpointEvent,即斷點(diǎn)記錄,
        BreakpointEvent breakpointEvent = (BreakpointEvent) event;
        //并通過斷點(diǎn)處的線程拿到線程幀,進(jìn)而獲取相應(yīng)的變量信息,并打印記錄。
        ThreadReference threadReference = breakpointEvent.thread();
        StackFrame stackFrame = threadReference.frame(0);
        List<LocalVariable> localVariables = stackFrame.visibleVariables();
        //輸出當(dāng)前線程棧幀保存的變量數(shù)據(jù)
        localVariables.forEach(t -> {
            Value value = stackFrame.getValue(t);
            System.out.println("local->" + value.type() + "," + value.getClass() + "," + value);
        });
    }

}

(3)代碼分析

【1】通過Bootstrap.virtualMachineManager();獲取連接器,客戶端即通過相應(yīng)的connector進(jìn)行連接,配置服務(wù)器程序ip地址和端口,連接后獲取對應(yīng)服務(wù)器的VM信息。

【2】通過VirtualMachine獲取類信息,通過遍歷獲取的類集合定位到目標(biāo)debug的類文件上

【3】對目標(biāo)類代碼特定位置設(shè)置并啟用斷點(diǎn)

【4】記錄斷點(diǎn)信息,阻塞服務(wù)器線程,并根據(jù)對應(yīng)事件獲取相應(yīng)的信息

【5】執(zhí)行event.resume釋放斷點(diǎn),服務(wù)器程序繼續(xù)運(yùn)行

(4)運(yùn)行測試

【1】啟動服務(wù)器程序,即上面的SpringBoot的web項(xiàng)目。本地以debug方式啟動調(diào)試器代碼,待會在這個(gè)位置看看獲取的信息,同時(shí)避免直接釋放了斷點(diǎn)。

【2】設(shè)置斷點(diǎn)位置為DebuggerApplication類的第22行

【3】啟動后測試該接口,可以發(fā)現(xiàn)服務(wù)器程序控制臺打印了如下結(jié)果。第22行還沒有執(zhí)行。

【4】此時(shí),在觀察調(diào)試器程序??梢钥吹将@取到了服務(wù)器程序棧幀的數(shù)據(jù)

【5】釋放斷點(diǎn),服務(wù)器正常運(yùn)行完本次請求處理流程

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • java 實(shí)現(xiàn)截取字符串并按字節(jié)分別輸出實(shí)例代碼

    java 實(shí)現(xiàn)截取字符串并按字節(jié)分別輸出實(shí)例代碼

    這篇文章主要介紹了java 實(shí)現(xiàn)截取字符串并按字節(jié)分別輸出實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • 關(guān)于@OnetoMany關(guān)系映射的排序問題,使用注解@OrderBy

    關(guān)于@OnetoMany關(guān)系映射的排序問題,使用注解@OrderBy

    這篇文章主要介紹了關(guān)于@OnetoMany關(guān)系映射的排序問題,使用注解@OrderBy,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • IDEA 重新導(dǎo)入依賴maven 命令 reimport的方法

    IDEA 重新導(dǎo)入依賴maven 命令 reimport的方法

    這篇文章主要介紹了IDEA 重新導(dǎo)入依賴maven 命令 reimport的相關(guān)知識,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Java設(shè)計(jì)模式之java觀察者模式詳解

    Java設(shè)計(jì)模式之java觀察者模式詳解

    這篇文章主要介紹了Java經(jīng)典設(shè)計(jì)模式之觀察者模式,簡單分析了觀察者模式的概念、原理并結(jié)合實(shí)例形式給出了java觀察者模式的具體用法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2021-09-09
  • Java多線程中的CyclicBarrier使用方法詳解

    Java多線程中的CyclicBarrier使用方法詳解

    這篇文章主要介紹了Java多線程中的CyclicBarrier使用方法詳解,CyclicBarrier是一種同步輔助工具,它允許一組線程都等待對方到達(dá)公共障礙點(diǎn),在涉及固定大小的線程的程序中,CyclicBarriers非常有用,這些線程間必須相互等待,需要的朋友可以參考下
    2023-12-12
  • IntelliJ IDEA 常用設(shè)置(配置)吐血整理(首次安裝必需)

    IntelliJ IDEA 常用設(shè)置(配置)吐血整理(首次安裝必需)

    這篇文章主要介紹了IntelliJ IDEA 常用設(shè)置(配置)吐血整理(首次安裝必需),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Java實(shí)戰(zhàn)房屋租賃網(wǎng)的實(shí)現(xiàn)流程

    Java實(shí)戰(zhàn)房屋租賃網(wǎng)的實(shí)現(xiàn)流程

    讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實(shí)現(xiàn)一個(gè)房屋租賃網(wǎng)站,大家可以在過程中查缺補(bǔ)漏,提升水平
    2021-11-11
  • 基于mybatis-plus-generator實(shí)現(xiàn)代碼自動生成器

    基于mybatis-plus-generator實(shí)現(xiàn)代碼自動生成器

    這篇文章專門為小白準(zhǔn)備了入門級mybatis-plus-generator代碼自動生成器,可以提高開發(fā)效率。文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-05-05
  • java 字符串分割的三種方法(總結(jié))

    java 字符串分割的三種方法(總結(jié))

    下面小編就為大家?guī)硪黄猨ava 字符串分割的三種方法(總結(jié))。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-11-11
  • Java數(shù)據(jù)庫連接池技術(shù)的入門教程

    Java數(shù)據(jù)庫連接池技術(shù)的入門教程

    這篇文章主要給大家介紹了關(guān)于Java數(shù)據(jù)庫連接池技術(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03

最新評論