前言
使用debug斷點調試是每個程式設計師的必會技能,我們每天的工作不就是寫bug,找別人的bug嘛!debug作為排查問題的一大絕殺技,斷點一打,問題分分鐘解決。再加上一些IDE的debug工具這麼好用,JDK自帶的jdb工具也能支持本地調試和遠程調試。妥妥的年度最佳工具,但是就我而言,平時排查問題就是打了個行斷點,條件斷點,雖然也能排查出大部分問題,但是總感覺使用的不夠 優雅~,而且對debug的過程也不清楚。
本文就來了解一下debug的原理和調試技巧。附帶git動圖演示,文中不會指出基礎的debug操作,只列出一些使用技巧,相信你掌握了這些技巧,對debug的理解更進一步,排查解決問題的效率也必然上一個檔次。
debug原理概述
JAVA調試器的運行,是由JPDA(Java Platform Debugger Architecture)體系支撐起來的。引用官方文檔中的闡述,JPDA 是一種多層調試架構,開發人員可以創建調試器程序,而且這些調試程序可以跨平台、跨虛擬機、跨JDK版本運行。參考oracle的 官方文檔
JPDA is a multi-tiered debugging architecture that allows tools developers to easily create debugger applications which run portably across platforms, virtual machine (VM) implementations and JDK versions.
整個JPDA體系架構如下圖所示,分為三個部分:
- Jvm TI(Java VM 工具接口) 是 J2SE 5.0 中引入的新接口,它取代了JVMDI。由C語言編寫,定義了VM提供的調試服務接口,用於獲取和控制當前虛擬機狀態。
- JDWP (Java 調試線協議) 由C語言編寫,定義了被調試者和調試器進程之間的通信數據格式。
- JDI ( Java 調試接口) 定義一個高級 Java 語言接口,調試工具的開發人員使用它來編寫調試器程序。IDEA的debug工具就是實現了這一套API,才為我們提供出來好用的工具。
Java遠程調試的原理是兩個VM之間使用JDWP協議通過socket進行通信,以達到遠程調試的目的,當我們在IDEA中以debug模式啟動運行類,就可以直接調試了,過程如下:
- IDEA客戶端和vm程序端 建立 socket 連接。使用JDWP協議。
- 將斷點位置創建了斷點事件通過 JDI 接口傳遞給服務端VM,VM調用suspend將VM掛起。
- VM 掛起之後將客戶端需要獲取的 VM 信息返回給客戶端,返回之後 VM 恢復運行狀態
- 客戶端獲取到 VM 返回的信息之後可以通過不同的方式展示給使用者。
下面通過一個動圖,看一下IDEA以debug模式運行程序都做了哪些事,首先建立起雙向的通信,應用程式通過debug模式運行起來之後,可以通過jps看到啟動參數中多了一行,這就是聲明了使用jdwp協議,以及一些vm參數。
-agentlib:jdwp=transport=dt_socket,address=127.0.0.1:**55951**,suspend=y,server=n
其實遠程debug也需要加上這段參數,如何進行遠程debug會在最後一個章節中提到,接下來列出一下常用的debug技巧。
斷點技巧
欄位斷點
如果想要知道某個類的欄位屬性在哪一行代碼被修改了值,就需要使用到欄位監視斷點。需要注意的是欄位監視斷點只能打在類欄位上,打在引用類型的對象或者集合類型上是不生效的。我們也可以為引用類型的對象屬性添加watch監視。
- 欄位監視斷點可以幫助我們定位到在哪一行被修改了。
- 屬性watch可以幫助我們捋清楚變量值的變化過程。
如下圖所示,我們為變量a上打了一個欄位監視斷點,debug運行時,則會停在改變a值得哪一行,我們也可以為這個a值添加watch,以觀察這個值變化鏈上的每一次值改變狀況。
方法斷點
當接口有多個實現時,當不知道程序執行走的是哪一個類的實現時,就可以將斷點打在接口中的方法上,那麼程序運行後可以直接步入到具體的實現類中的方法。
有時候不想在方法裡一步步執行,可以直接在方法簽名上打斷點,斷點會在方法的首尾行停留,配合上watch,我們可以看這個方法中究竟做了什麼,以及產生了哪些影響。
條件斷點
常用於多線程或者請求頻繁時,當不斷有請求調用當前方法時,我們可以設置條件斷點,滿足條件時,才會步入斷點行,然後對當前請求進一步調試。條件斷點怎麼打就不用多說了,右鍵斷點,添加條件判斷語句,只有true和false兩種結果,條件是根據上下文寫的。
異常斷點
異常斷點在工作中也非常有用,就是因為遇到了異常才去調試程序,那怎麼能不用上異常斷點呢?平時看到長串的異常堆棧信息,例如最常見的空指針異常,我們都是找到對應是哪一行爆出來拋出的異常,然後慢慢往下跟,找到具體為null的變量,才能找出導致程序退出的根因。
IDEA的debug工具,提供的異常斷點,顧名思義,就是當遇到異常的時候,斷點會停在當前行,使用起來也非常簡單,只需要添加進對應的異常類型,開啟異常斷點即可。如下圖在空指針異常時斷點。以供查看上下文變量的值。
多線程斷點
當遇到多線程調試的時候也是個麻煩事兒,因為不知道當前到底是哪一個線程在運行中,所以就需要使用到線程debug了,也是結合條件斷點,根據線程名去斷下對應的線程。
Stream流斷點
大家常用的stream流,寫時一時爽,調試火葬場,因為屬於流操作,斷點無法跟蹤到內部,查看相應的值變化,所以當stream複雜的時候,debug起來也是一件頭疼的事,但是idea中的跟蹤流鏈功能就非常實用了,他能顯示出每一步流操作的輸出結果。
遠程斷點
本地debug調試自然是簡單,但是一旦項目部署到伺服器上,沒有了本地環境,該怎麼調試呢?其實有辦法,線上的項目可以開啟遠程調試,如本文開頭所講,其實debug的原理就是兩個vm之間的socket通信,加上一串參數就好了,那麼我們也可以給線上的服務運行的JAVA_OPTS中加上這段參數,聲明好埠即可,例如:
-agentlib:jdwp=transport=dt_socket,address=127.0.0.1:9999,suspend=y,server=n
服務端的vm啟動好之後,接著啟動客戶端的vm,當我們使用IDEA,可以創建一個remote jvm debugger的客戶端,用於attach到遠程jvm內部,過程如下所示,可以做到如同本地調試一樣,調試遠程服務了,但是需要注意的是本地的代碼一定要和線上運行服務的那一套代碼完全相同。
注意事項
- 一般正式環境不會將遠程debug開啟,會有安全隱患。
- 在測試環境遠程debug時會將整個vm掛起,所以服務處於不可用的階段,會影響到其他開發人員正常使用服務。
- debug時間太長,可能會使vm宕掉。所以切記一定要及時關掉debug的客戶端。
如果本文對你有幫助,別忘記給我個3連問 ,點讚,轉發,評論,
學習更多JAVA知識與技巧,關注與私信博主(08)學習JAVA 課件,源碼,安裝包,還有最新大廠面試資料等等等
咱們下期見。
收藏 等於白嫖,點讚才是真情。