有趣的BUG之Stack Overflow

Liming_Code發表於2022-05-23

今天遇到一個很有意思的bug,當程式開發完成後打包到伺服器執行,總是會出現棧溢位異常,經過排查發現,問題出現在一個介面上,但這個介面邏輯並不複雜,除了幾局邏輯程式碼外和列印語句之外也沒有其他的了,但是隻要呼叫這個介面就馬上會出現棧溢位的異常,隨後對程式碼進行了排查,最後發現問題居然出現在日誌列印語句上。

平常在開發過程中我們經過會通知列印日誌來了解程式的執行狀態,沒想到今天在這上面翻了車,這個列印語句是列印一個物件,我們知道,當要列印一個物件的時候會自動呼叫物件的toString()方法,專案中我們會使用lombok外掛來幫助我們生成對應的get、set、equals、hashcode、toString等方法,而列印的這個物件在由Spring容器管理的一個bean,我們給這個bean取名A,在A裡面又依賴注入了另一個bean,我們取名B,A和B形成了迴圈依賴,且都重寫了toString方法,所以當呼叫A的toString方法時,會呼叫B的toString方法,當呼叫B的toString又會呼叫A的toString,由此形成了死迴圈,最終導致了棧溢位的異常。
下面是我精簡出的出現問題的程式碼結構,幫助大家避坑
類A結構

public class A {

    private B b;

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }

    @Override
    public String toString() {
        return "A{" +
                "b=" + b +
                '}';
    }
}

類B結構

public class B {

    private A a;

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }

    @Override
    public String toString() {
        return "B{" +
                "a=" + a +
                '}';
    }
}

Spring配置檔案,類A類B交給容器管理

<bean id="a" class="com.zlm.bean.A">
    <property name="b" ref="b"/>
</bean>
<bean id="b" class="com.zlm.bean.B">
    <property name="a" ref="a"/>
</bean>

主程式

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext xmlApplicationContext = new ClassPathXmlApplicationContext("bean.xml");
        A bean = xmlApplicationContext.getBean(A.class);
        // 會報錯
        System.out.println(bean);
    }
}

錯誤資訊

Exception in thread "main" java.lang.StackOverflowError

相關文章