1. 程式人生 > 其它 >IDEA Debug 小技巧

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有兩個具體的實現:ServiceAServiceB,分別實現了介面的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 至少有一個選項存在。

  1. Emulated:用於提高除錯效能
  2. Method entry:進入方法時啟用斷點
  3. Method exit:退出方法時啟用斷點

屬性斷點

在屬性的行頭點選即可新增一個小眼睛一樣的屬性監聽斷點,用於監聽某個屬性的讀寫變化過程;

更多功能點

  • Condition

    用於輸入表示式,進行過濾

  • Watch

    1. Filed Access:讀取此屬性時(寫入時不管)

    2. Filed modification:寫入此屬性時(讀取時不管)

異常斷點

異常斷點是開發、除錯的時候經常用到的一個功能,用於快速定位到那行程式碼出現了異常;

設定方式:

  • 第一步,使用快捷鍵Ctrl + Shift + F8開啟配置視窗;
  • 第二步,點選左上角的+號;
  • 第三步,選擇Java Exception Breakpoints
  • 第四步,新增需要斷點的異常,如:ArithmeticException
  • 第五步,Debug執行,當出現指定的異常時,就會進入斷點;

更多功能點

  • Condition

    用於輸入表示式,進行過濾

  • Watch

    1. Caught excetion:只有當你自己try-catch了這個異常才會啟用斷點

    2. Uncaught excetion:只有當你自己不try-catch時才會啟用斷點

臨時斷點

臨時斷點是指只觸發一次的斷點,之後就自動取消了;一般用於特定的場合下需要確認值是符合我們的預期,完了之後就不在需要了;

設定及演示過程如下

設定方式:

  • 第一步,設定斷點
  • 第二步,使用快捷鍵Ctrl + Shift + F8開啟配置視窗;
  • 第三步,找到對應的斷點;
  • 第四步,勾選Remove once hit
  • 第五步,Debug執行,當斷點觸發一次之後,就自動取消了;

斷點條件

設定斷點的觸發條件,也是閱讀原始碼、修復Bug經常用到的一個功能,比如讀Spring原始碼,研究Bean生命週期的時候,就可以根據Bean的name去設定斷點條件,用來判斷之後在操作指定物件的時候,才進入斷點;

如下示例:

  1. for迴圈之後只有i是2的倍數時,才進入斷點,可以在Conditon中填入i % 2 == 0

  2. 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功能,但是這並不是所有的,一些不常用功能可以根據需要選擇使用

總結,工欲善其事必先利其器,利用好工具,就能做到事半功倍。