Java異常(exception)性能優化

在Java中,構造異常對象是”十分”耗時的,其原因是在默認情況下,創建異常對象時會調用父類Throwable的fillInStackTrace()方法生成棧追蹤信息,JDK中的源碼如下:

public synchronized Throwable fillInStackTrace() {
        if (stackTrace != null ||
            backtrace != null /* Out of protocol state */ ) {
            fillInStackTrace(0); // native方法
            stackTrace = UNASSIGNED_STACK;
        }
        return this;
    }

在我自己做的測試中,new一個帶有棧追蹤信息的Exception對象要比創建不帶追蹤信息的對象慢50倍以上。雖然打印調用棧能夠精確定位到錯誤發生的代碼所在行,但我們可以考慮一下,真的有必要讓所有異常都生成這些信息嗎?

我們在開發業務系統的過程中一般都會使用異常機制來實現錯誤處理邏輯,這些異常通常都可以分成兩大類:

業務異常
這些是我們自定義的、可以預知的異常,拋出這種異常並不表示系統出了問題,而是正常業務邏輯上的需要,例如用戶名密碼錯誤、參數錯誤等。

系統異常
往往是運行時異常,比如數據庫連接失敗、IO失敗、空指針等,這種異常的產生多數表示系統存在問題,需要人工排查定位。

其實對於業務異常,我們只需要簡單的知道一個描述問題的字符串即可,棧追蹤信息對我們的意義並不大。而對於系統異常,追蹤信息纔是排查錯誤不可或缺的參考。因此我們可以想辦法控制一下,創建業務異常時不生成調用棧追蹤信息以降低開銷,系統異常則正常生成。

其實方法非常簡單,在我們自定義異常時,只需要重寫父類的一個帶有4個參數的構造方法即可,此方法在Exception和RuntimeException類中都存在:

> protected RuntimeException(String message, Throwable cause,
>                                boolean enableSuppression,
>                                boolean writableStackTrace) {
>         super(message, cause, enableSuppression, writableStackTrace);
>     }

這幾個參數的意義如下:

message
異常的描述信息,也就是在打印棧追蹤信息時異常類名後面緊跟着的描述字符串
cause
導致此異常發生的父異常,即追蹤信息裏的caused by
enableSuppress
關於異常掛起的參數,這裏我們永遠設爲false即可
writableStackTrace
表示是否生成棧追蹤信息,只要將此參數設爲false, 則在構造異常對象時就不會調用fillInStackTrace()

例如,業務異常可以這樣定義:

public class XXXException extends RuntimeException {
/**
* 僅包含message, 沒有cause, 也不記錄棧異常, 性能最高
* @param msg
*/
public XXXException(String msg) {
this(msg, false);
}

/**
 * 包含message, 可指定是否記錄異常
 * @param msg
 * @param recordStackTrace
 */
public EngineException(String msg, boolean recordStackTrace) {
    super(msg, null, false, recordStackTrace);
}

/**
 * 包含message和cause, 會記錄棧異常
 * @param msg
 * @param cause
 */
public EngineException(String msg, Throwable cause) {
    super(msg, cause, false, true);
}

即通過使用父類中4參數的構造方法精確控制異常類的行爲。當我們想要創建”輕量級”異常時,使用第一個構造方法即可;如果我們想將系統級異常封裝成一下,並希望在日誌中打印棧追蹤時,就使用第三個構造方法。

PS: 只有在高併發系統中做上述優化纔會有明顯效果。如果拋異常不頻繁的話也不會有明顯效果,因爲即便是慢50倍,實際也是納秒級的區別,對一個請求處理來說微不足道。

作者:司青
來源:CSDN
原文:https://blog.csdn.net/neosmith/article/details/82626960

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章