IDEA Debug 小技巧
IDEA的Debug控制檯在整個視窗的左下方:
IDEA 除錯功能說明
Show Execution Point
快捷鍵:Alt + F10
回到當前啟用的斷點處;當你的滑鼠不在斷點所處的行,點選之後,會立馬復位到斷點處;
Step Ove
快捷鍵:F8
單步除錯;逐行往下執行,如果執行行有其他方法,不會進入對應的方法;日常Debug用的最多的一個功能
Step Into
快捷鍵:F7
進入方法體內部。該功能會進入自定義的方法或者三方庫的方法;注意,不會進入JDK的方法;
Force Step Into
快捷鍵:Alt + Shift + F7
強制進入方法體內部,與Step Into
不同的是,會進入JDK的方法;
Step Out
快捷鍵:Shift + F8
跳出方法體;一般會配合(Force)Step Into
一起使用
Drop frame
回到方法的呼叫處,同時上下文內所有的變數的值也回到那個時候。
該按鈕能夠點選的前提條件是:當前所處的方法有上級方法,如果你是main方法裡,那麼按鈕就是灰色,無法點選;
Run to Cursor
快捷鍵:Alt + F9
將程式碼執行到游標處,游標停在哪裡就執行到哪裡;
Evaluate Expression
快捷鍵:Alt + F8
表示式計算器;可執行任意合法的表示式。
Trace Current Stream Chain
追蹤當前Stream流;只有在Stream程式碼上,此按鈕才會亮起。
Rerun Main
快捷鍵:Ctrl + F5
查詢執行Debug;
Resume Program
快捷鍵:F9
恢復程式;當因為斷點導致程式碼停止之後,此功能可以讓持續恢復執行;有下一個斷點時,會跳轉到下一個斷點;沒有下一個斷點,會執行到持續結束;
Stop Main
快捷鍵:Ctrl + F12
停止程式;
View Breakpoints
快捷鍵:Ctrl + Shift + F8
開啟斷點管理視窗;
Mute Breakpoints
停用所有的斷點;
Get Thread Dump
拿到當前執行緒的Dump,可以檢視當前執行緒的狀態;
篩選
除錯小技巧
行斷點
行斷點的圖示是一個 圓形的紅點,在需要斷點的程式碼行頭點選,即可新增斷點
方法斷點
將斷點打在某個具體的方法上,方法執行的時候,會進入斷點;
舉個方法除錯最常用的Debug場景:
當閱讀原始碼或者自己寫業務需求的時候,經常會用到策略、模板方法等設計模式;在除錯的時候,需要知道,當前介面方法或者抽象方法的執行,到底是走的哪一個具體的實現,用方法除錯就能很方便的找到;如下示例;
介面Service
有兩個具體的實現:ServiceA
和ServiceB
,分別實現了介面的method
方法,除錯的過程中就可以將斷點打在介面的method方法上;當我們在Main方法中例項化了ServiceB,斷點就自動進入到ServiceB的method()方法了;
// 介面Service public interface Service { void method(); } // 實現類 ServiceA public class ServiceA implements Service{ @Override public void method() { System.out.println("Service A"); } } // 實現類 ServiceB public class ServiceB implements Service{ @Override public void method() { System.out.println("Service B"); } } // Main 方法 public class Main { public static void main(String[] args) { Service serviceB = new ServiceB(); serviceB.method(); } }
更多功能點
-
Condition
用於輸入表示式,進行過濾
-
Watch
Method entry 和 Method exit 至少有一個選項存在。
- Emulated:用於提高除錯效能
- Method entry:進入方法時啟用斷點
- Method exit:退出方法時啟用斷點
屬性斷點
在屬性的行頭點選即可新增一個小眼睛一樣的屬性監聽斷點,用於監聽某個屬性的讀寫變化過程;
更多功能點
-
Condition
用於輸入表示式,進行過濾
-
Watch
-
Filed Access:讀取此屬性時(寫入時不管)
-
Filed modification:寫入此屬性時(讀取時不管)
-
異常斷點
異常斷點是開發、除錯的時候經常用到的一個功能,用於快速定位到那行程式碼出現了異常;
設定方式:
- 第一步,使用快捷鍵
Ctrl + Shift + F8
開啟配置視窗; - 第二步,點選左上角的
+
號; - 第三步,選擇
Java Exception Breakpoints
; - 第四步,新增需要斷點的異常,如:
ArithmeticException
; - 第五步,Debug執行,當出現指定的異常時,就會進入斷點;
更多功能點
-
Condition
用於輸入表示式,進行過濾
-
Watch
-
Caught excetion:只有當你自己try-catch了這個異常才會啟用斷點
-
Uncaught excetion:只有當你自己不try-catch時才會啟用斷點
-
臨時斷點
臨時斷點是指只觸發一次的斷點,之後就自動取消了;一般用於特定的場合下需要確認值是符合我們的預期,完了之後就不在需要了;
設定及演示過程如下
設定方式:
- 第一步,設定斷點
- 第二步,使用快捷鍵
Ctrl + Shift + F8
開啟配置視窗; - 第三步,找到對應的斷點;
- 第四步,勾選
Remove once hit
; - 第五步,Debug執行,當斷點觸發一次之後,就自動取消了;
斷點條件
設定斷點的觸發條件,也是閱讀原始碼、修復Bug經常用到的一個功能,比如讀Spring原始碼,研究Bean生命週期的時候,就可以根據Bean的name去設定斷點條件,用來判斷之後在操作指定物件的時候,才進入斷點;
如下示例:
-
for迴圈之後只有i是2的倍數時,才進入斷點,可以在Conditon中填入
i % 2 == 0
; -
0-10000的迴圈,當i等於5000的時候,進入斷點,其他的時候忽略,可以在Conditon中填入
i == 5000
設定過程:
- 第一步,設定斷點
- 第二步,右鍵斷點處,開啟操作介面
- 第三步,輸入表示式,比如迴圈時只有偶數才斷點,可以輸入
i % 2 == 0
模擬異常
開發過程中,有時候需要人為製造一些異常,比如事務操作(@Transactional),需要驗證是否能達到回滾的效果;
比如下面的虛擬碼:
// 虛擬碼 假如這裡是個事務操作 // @Transactional public void save() throws RuntimeException{ table1Save(); // 我先在這裡測試一下,異常之後,是否會滾 //throw new RuntimeException("異常了"); table2Save(); table3Save(); } void table1Save(){} void table2Save(){} void table3Save(){}
當咱希望在執行table2Save的時候,拋個異常,讓整個操作回滾,通常的做法是會在程式碼中人為拋一個異常:thrownewRuntimeException("異常了");
這樣做並沒有什麼錯,但是不是很優雅,而且還存在一下的兩個問題:
-
只能在方法的末尾拋異常;流程中間拋,後面的程式碼會報錯
-
有風險
這種業務功能中人為拋異常,如果一不小心忘記刪除,將這個異常提交上去,就是人為的生產事故,可能帶來比較嚴重的後果;
IDEA優雅模擬異常
那有沒有什麼更好的方式呢?IDEA給我們提供了更加優化的模擬異常方案,不用寫異常程式碼,可以利用工具直接丟擲異常,操作步驟如下:
操作步驟:
- 第一步,在要模擬異常的地方加上斷點;
- 第二步,Debug模式執行程式碼並進入斷點;
- 第三步,Frames中找到對應的斷點記錄;
- 第四步,右鍵,選擇
Throw Execption
; - 第五步,輸入你想丟擲的異常,點選
ok
,即可丟擲對應的異常;
多執行緒除錯
多執行緒開發的時候,執行緒的排程策略並不由程式碼控制,導致斷點除錯的過程可能會線上程間跳來跳去,如果邏輯複雜點,跳著跳著可能就矇蔽了;如下示例:
public class Main { public static void main(String[] args) { System.out.println("0 main start"); new Thread(() -> { System.out.println("1 hello"); }, "thread1").start(); new Thread(() -> { System.out.println("2 world"); }, "thread2").start(); System.out.println("3 main end"); } }
如果把斷點打在System.out.println
上,除了0是能保證第一個輸出的,1、2、3的執行順序是沒辦法保證的;
預設情況下,斷點的suspend
設定是all
,順序並不固定;
如果將所有斷點的suspend
設定為Thread
之後,就會按著執行緒的順序,逐個去執行:
修改變數
在斷點的過程中去修改某個變數的值;常見的場景是:當某個變數,因為邏輯bug導致屬性值和預期的不一樣,但是又不想從頭再debug一遍,就可以直接在除錯的過程中修改成預期的值,並繼續執行後續的步驟;
斷點執行程式碼/方法/表示式
斷點過程中,可以執行一段表示式、程式碼或者方法
1:程式碼
2:方法執行
3:表示式
遠端除錯
非常實用一個技能,當線上程式碼出現Bug之後,可以通過此方式用原生代碼進行遠端除錯,快速定位問題並修復;
注意:遠端除錯必須保證原生代碼和線上程式碼版本一致,否則不會進入斷點;
設定步驟如下:
1:新增一個用於遠端除錯的介面
@RestController public class RemoteDebugController { @GetMapping("debug") public Integer debug(Integer p){ System.out.println(p); return p; } }
2:將程式碼打成jar包
mvn clean package -Dmaven.test.skip=true
3:IDEA 設定遠端除錯啟動項
以下時幾個重要引數的說明
- Name:名稱,不重要,隨意填寫;
- Host:遠端服務所處的機器IP,這裡是本機測試,所以填寫127.0.0.1即可,實際使用填寫遠端服務所處的真實IP
- PORT:遠端除錯的埠
- 遠端服務執行時的JVM引數:IDEA 工具幫我們生成的服務執行時需要新增的JVM引數,直接複製使用即可;
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5555
4:啟動專案
為了演示,這裡就不在IDEA中啟動了,直接在CMD視窗下啟動測試專案,記得用上上面生成的引數
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5555 -jar spring-boot-001-hello-world-0.0.1-SNAPSHOT.jar
啟動過程出現socket的監聽日誌,說明正常
Listening for transport dt_socket at address: 5555
5:IDEA 遠端除錯配置
6:測試
左側為jar包執行的控制檯;右上方為IDEA的介面;右下方為瀏覽器,模擬客戶端請求;如下圖示:
當客戶端發起請求的時候,IDEA就會進入斷點,當執行通過,可以看出,左側控制檯就會打印出對應的日誌;
線上除錯,務必要給斷點加上條件,比如特定測試賬號才進去斷點;避免讓真是使用者的請求也進入斷點,影響使用者的使用;
通過此方式,如果遠端的程式碼有bug,就可以直接在本地的IDEA工具中進行除錯,非常的方便;
更多功能
上面列舉了絕大部分常用的Debug功能,但是這並不是所有的,一些不常用功能可以根據需要選擇使用
總結,工欲善其事必先利其器,利用好工具,就能做到事半功倍。