全部的程序員都會遇到 bug,對於運行態的錯誤,咱們每每須要一些方法來觀察和測試運行態中的環境。在 Java 程序中,最簡單的,您是否嘗試過使用 System.out.println()
來輸出您的 Java 程序的執行中的各類變量狀態來發現您的 Java 程序運行時的問題?這種方式方便易用,在一些簡單的狀況下可以解決您的問題,可是若是當您的程序運行在遠程環境上,或者當前環境不容許控制檯終端輸出(好比,考慮一下虛擬機初始化之時),您沒法獲取終端輸出的時候呢?或者,若是您根本沒法本地修改運行您的程序?前端
無須擔憂,您能夠經過不少的調試工具來幫助您解決這個問題,常見的 IDE 都附帶一個很是直觀簡單的調試工具,好比 Eclipse(圖 1)就提供一個功能很是全面,操做很是簡單的調試器。程序員
其餘的一些常見的 Java IDE,好比 Netbeans 和 IntelliJ 等等也都提供了相似的功能,您甚至能不用 IDE 提供的圖形界面,使用 JDK 自帶的 jdb 工具,以文本命令的形式來調試您的 Java 程序。這些形形色色的調試器都支持本地和遠程的程序調試,那麼它們是如何被開發的?它們之間存在着什麼樣的聯繫呢?咱們不得不說起 Java 的調試體系—— JPDA 。算法
咱們知道,Java 程序都是運行在 Java 虛擬機上的,咱們要調試 Java 程序,事實上就須要向 Java 虛擬機請求當前運行態的狀態,並對虛擬機發出必定的指令,設置一些回調等等,那麼 Java 的調試體系,就是虛擬機的一整套用於調試的工具和接口。apache
對於 Java 虛擬機接口熟悉的人來講,您必定還記得 Java 提供了兩個接口體系,JVMPI(Java Virtual Machine Profiler Interface)和 JVMDI(Java Virtual Machine Debug Interface),而它們,以及在 Java SE 5 中準備代替它們的 JVMTI(Java Virtual Machine Tool Interface),都是 Java 平臺調試體系(Java Platform Debugger Architecture,JPDA)的重要組成部分。 Java SE 自 1.2.2 版就開始推出 Java 平臺調試體系結構(JPDA)工具集,而從 JDK 1.3.x 開始,Java SDK 就提供了對 Java 平臺調試體系結構的直接支持。顧名思義,這個體系爲開發人員提供了一整套用於調試 Java 程序的 API,是一套用於開發 Java 調試工具的接口和協議。本質上說,它是咱們通向虛擬機,考察虛擬機運行態的一個通道,一套工具。理解這一點對於學習 JPDA 很是重要。編程
換句話說,經過 JPDA 這套接口,咱們就能夠開發本身的調試工具。經過這些 JPDA 提供的接口和協議,調試器開發人員就能根據特定開發者的需求,擴展定製 Java 調試應用程序,開發出吸引開發人員使用的調試工具。前面咱們提到的 IDE 調試工具都是基於 JPDA 體系開發的,區別僅僅在於它們可能提供了不一樣的圖形界面、具備一些不一樣的自定義功能。另外,咱們要注意的是,JPDA 是一套標準,任何的 JDK 實現都必須完成這個標準,所以,經過 JPDA 開發出來的調試工具先天具備跨平臺、不依賴虛擬機實現、JDK 版本無關等移植優勢,所以大部分的調試工具都是基於這個體系的。後端
JPDA 定義了一個完整獨立的體系,它由三個相對獨立的層次共同組成,並且規定了它們三者之間的交互方式,或者說定義了它們通訊的接口。這三個層次由低到高分別是 Java 虛擬機工具接口(JVMTI),Java 調試線協議(JDWP)以及 Java 調試接口(JDI)。這三個模塊把調試過程分解成幾個很天然的概念:調試者(debugger)和被調試者(debuggee),以及他們中間的通訊器。被調試者運行於咱們想調試的 Java 虛擬機之上,它能夠經過 JVMTI 這個標準接口,監控當前虛擬機的信息;調試者定義了用戶可以使用的調試接口,經過這些接口,用戶能夠對被調試虛擬機發送調試命令,同時調試者接受並顯示調試結果。在調試者和被調試着之間,調試命令和調試結果,都是經過 JDWP 的通信協議傳輸的。全部的命令被封裝成 JDWP 命令包,經過傳輸層發送給被調試者,被調試者接收到 JDWP 命令包後,解析這個命令並轉化爲 JVMTI 的調用,在被調試者上運行。相似的,JVMTI 的運行結果,被格式化成 JDWP 數據包,發送給調試者並返回給 JDI 調用。而調試器開發人員就是經過 JDI 獲得數據,發出指令。圖 2 展現了這個過程:緩存
固然,開發人員徹底能夠不使用完整的三個層次,而是基於其中的某一個層次開發本身的應用。好比您徹底能夠僅僅依靠經過 JVMTI 函數開發一個調試工具,而不使用 JDWP 和 JDI,只使用本身的通信和命令接口。固然,除非是有特殊的需求,利用已有的實現會使您事半功倍,避免重複發明輪子。網絡
這三個模塊咱們會在後續文章中分別詳細介紹,這裏咱們簡單介紹它們的主要功能:架構
JVMTI(Java Virtual Machine Tool Interface)即指 Java 虛擬機工具接口,它是一套由虛擬機直接提供的 native 接口,它處於整個 JPDA 體系的最底層,全部調試功能本質上都須要經過 JVMTI 來提供。經過這些接口,開發人員不只調試在該虛擬機上運行的 Java 程序,還能查看它們運行的狀態,設置回調函數,控制某些環境變量,從而優化程序性能。咱們知道,JVMTI 的前身是 JVMDI 和 JVMPI,它們原來分別被用於提供調試 Java 程序以及 Java 程序調節性能的功能。在 J2SE 5.0 以後 JDK 取代了 JVMDI 和 JVMPI 這兩套接口,JVMDI 在最新的 Java SE 6 中已經不提供支持,而 JVMPI 也計劃在 Java SE 7 後被完全取代。eclipse
JDWP(Java Debug Wire Protocol)是一個爲 Java 調試而設計的一個通信交互協議,它定義了調試器和被調試程序之間傳遞的信息的格式。在 JPDA 體系中,做爲前端(front-end)的調試者(debugger)進程和後端(back-end)的被調試程序(debuggee)進程之間的交互數據的格式就是由 JDWP 來描述的,它詳細完整地定義了請求命令、迴應數據和錯誤代碼,保證了前端和後端的 JVMTI 和 JDI 的通訊通暢。好比在 Sun 公司提供的實現中,它提供了一個名爲 jdwp.dll(jdwp.so)的動態連接庫文件,這個動態庫文件實現了一個 Agent,它會負責解析前端發出的請求或者命令,並將其轉化爲 JVMTI 調用,而後將 JVMTI 函數的返回值封裝成 JDWP 數據發還給後端。
另外,這裏須要注意的是 JDWP 自己並不包括傳輸層的實現,傳輸層須要獨立實現,可是 JDWP 包括了和傳輸層交互的嚴格的定義,就是說,JDWP 協議雖然不規定咱們是經過 EMS 仍是快遞運送貨物的,可是它規定了咱們傳送的貨物的擺放的方式。在 Sun 公司提供的 JDK 中,在傳輸層上,它提供了 socket 方式,以及在 Windows 上的 shared memory 方式。固然,傳輸層自己無非就是本機內進程間通訊方式和遠端通訊方式,用戶有興趣也能夠按 JDWP 的標準本身實現。
JDI(Java Debug Interface)是三個模塊中最高層的接口,在多數的 JDK 中,它是由 Java 語言實現的。 JDI 由針對前端定義的接口組成,經過它,調試工具開發人員就能經過前端虛擬機上的調試器來遠程操控後端虛擬機上被調試程序的運行,JDI 不只能幫助開發人員格式化 JDWP 數據,並且還能爲 JDWP 數據傳輸提供隊列、緩存等優化服務。從理論上說,開發人員只需使用 JDWP 和 JVMTI 便可支持跨平臺的遠程調試,可是直接編寫 JDWP 程序費時費力,並且效率不高。所以基於 Java 的 JDI 層的引入,簡化了操做,提升了開發人員開發調試程序的效率。
表 1 總結了三個模塊的不一樣點:
Apache Harmony 旨在開發出一個獨立且與現有 JDK 兼容的 Java SE 5 實現,它以 Apache 軟件許可證 2.0 版發行。它創建了一個開放的模塊化運行時架構,包括虛擬機和類庫之間及其內部的模塊化,經過這個平臺,社區能在已有實現的基礎上自由定製本身的 Java 實現,或者對某個模塊單獨進行創新。
每個虛擬機都應該實現 JVMTI 接口,可是 JDWP 和 JDI 自己與虛擬機並不是是不可分的,這三個層之間是經過標準所定義的交互的接口和協議聯繫起來的,所以它們能夠被獨立替換或取代,但不會影響到總體調試工具的開發和使用。所以,開發和使用本身的 JDWP 和 JDI 接口實現是可能的。
Java 軟件開發包(SDK)標準版裏提供了 JPDA 三個層次的標準實現,事實上,調試工具開發人員還有不少其餘開源實現能夠選擇,好比 Apache Harmony 提供了 JDWP 的實現。而 JDI,咱們能夠在 Eclipse 一個子項目 org.eclipse.jdt.debug 裏找到其完整的實現(Harmony 也使用了這套實現,做爲其 J2SE 類庫的一部分)。經過標準協議,Eclipse IDE 的調試工具就能夠徹底在 Harmony 的環境上運行。
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 由三個獨立的模塊 JVMTI、JDWP、JDI 組成。
調試者經過 JDI 發送接受調試命令。
JDWP 定義調試者和被調試者交流數據的格式。
JVMTI 能夠控制當前虛擬機運行狀態。
除了標準實現,JPDA 還有許多開源實現供使用。
Java 調試工具的優勢。
JDI— Java 調試接口(Java Debug Interface)
JDT— Java 開發工具(Java Development Tools)
JDWP— Java 調試網絡協議(Java Debug Wire Protocol)
JPDA— Java 平臺調試器架構(Java Platform Debugger Architecture)
JVM— Java 虛擬機(Java Virtual Machine)
JVMDI— JVM 調試接口(JVM Debug Interface)
JVMTI— JVM 工具接口(JVM Tool Interface)
VM— 虛擬機(Virtual Machine)