阿里雙十一的性能兇手之一:自定義異常爲何性能差(求點贊)

IT 老哥

一個在大廠作高級Java開發的程序猿前端

關注微信公衆號:IT 老哥java

回覆:Java實戰項目視頻教程:便可獲取200G,27套實戰項目視頻教程程序員

回覆:Java 學習路線,便可獲取最新最全的一份學習路線圖web

回覆:Java 電子書,便可領取 13 本頂級程序員必讀書籍spring

回覆:Java 全套教程,便可領取:Java 基礎、Java web、JavaEE 所有的教程,包括 spring boot 等數據庫

回覆:簡歷模板,便可獲取 100 份精美簡歷api

老哥嗶嗶叨

你們應該都經歷過雙十一吧,那個流量大的恐怖吧,那個併發高的嚇人吧。那麼在一個高併發的系統裏,有哪些點是影響系統性能的呢,今天咱們來說其中一個點:自定義異常微信

瘋狂的異常

爲何異常會影響性能

首先給你們看一段JDKThrowable源碼markdown

public synchronized Throwable fillInStackTrace() {
    if (stackTrace != null ||
        backtrace != null /* Out of protocol state */ ) {
        fillInStackTrace(0);
        stackTrace = UNASSIGNED_STACK;
    }
    return this;
  }
複製代碼

上面這段JDK的源碼就是拋出異常時會調用的方法,這段方法暴露出兩個問題併發

  • 使用了synchronized修飾整個異常方法
  • 將異常追蹤信息放到了堆棧中(想一想JVM線程

異常種類

  • 業務異常

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

  • 系統異常

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

相信你們都接觸過異常,對於業務異常,咱們只須要簡單的知道一個描述問題的字符串便可,棧追蹤信息對咱們的意義並不大。而對於系統異常,追蹤信息纔是排查錯誤不可或缺的參考。

你們試想,若是前端傳的參數錯了,系統裏就拋出一個異常,那麼在雙十一的狀況下一秒鐘得拋出多少個異常呢?

問題思考

  • 拋異常的時候是否是會被 synchronized 上同步鎖?
  • 需不須要線程去執行?
  • 是否是得建立異常對象?
  • 需不須要堆棧去存儲?
  • 需不須要 jvm 去垃圾回收?

性能測試

  • 建立普通 Java 對象 (CustomObject extends HashMap)

  • 建立普通 Java 異常對象(CustomException extends Exception)

  • 建立改進的 Java 業務異常對象 (CustomException extends Exception,覆寫 fillInStackTrace 方法,而且去掉同步)

測試結果

(運行環境:xen 虛擬機,5.5G 內存,8 核;jdk1.6.0_18)

(10 個線程,建立 10000000 個對象所需時間)

  • 普通 Java 對象:45284 MS
  • 普通 java 異常:205482 MS
  • 改進的 Java 業務異常:16731 MS

你們能夠看到正常拋出 Exception 的和覆寫了 fillInStackTrace 的 Exception,性能差距了不少倍,若是高併發的系統裏,就像雪球同樣越滾越大。影響系統的併發量。

解決方案:覆寫 fillInStackTrace

咱們來看看很是 NB 的kafka源碼是如何優化的。

/* avoid the expensive and useless stack trace for api exceptions */
/* 翻譯:避免對api異常進行昂貴且無用的堆棧跟蹤 */
@Override
public Throwable fillInStackTrace() {
    return this;
}
複製代碼

不少開源框架都是這樣處理,避免沒必要要的性能浪費。

老哥結語

什麼是匠人精神,就是將一件事情作到極致。優化永無止境,且行且珍惜。

相關文章
相關標籤/搜索