強制要求JVM始終拋出含堆棧的異常(-XX:-OmitStackTraceInFastThrow)


線上環境異常沒有打印棧信息解決思路:

問題描述:

生產環境拋異常,log.error("這里發生錯誤", e),但卻沒有將堆棧信息輸出到日志文件(在本地調試是有的,無法復現),導致定位問題無法准確定位到代碼行。

問題分析:

它跟JDK5的一個新特性有關,即jvm啟動參數-XX:-OmitStackTraceInFastThrow,參數:OmitStackTraceInFastThrow字面意思是省略異常棧信息從而快速拋出。對於一些頻繁拋出的異常,JDK為了性能會做一個優化,即JIT重新編譯后會拋出沒有堆棧的異常,而在使用-server模式時,該優化選項是開啟的,因此在頻繁拋出某個異常一段時間后,該優化開始起作用,即只拋出沒有堆棧的異常信息。
相關源碼:
  // If this throw happens frequently, an uncommon trap might cause
  // a performance pothole.  If there is a local exception handler,
  // and if this particular bytecode appears to be deoptimizing often,
  // let us handle the throw inline, with a preconstructed instance.
  // Note:   If the deopt count has blown up, the uncommon trap
  // runtime is going to flush this nmethod, not matter what.
  // 這里要滿足兩個條件:1.檢測到頻繁拋出異常,2. OmitStackTraceInFastThrow為true,或StackTraceInThrowable為false
  if (treat_throw_as_hot
      && (!StackTraceInThrowable || OmitStackTraceInFastThrow)) {
    // If the throw is local, we use a pre-existing instance and
    // punt on the backtrace.  This would lead to a missing backtrace
    // (a repeat of 4292742) if the backtrace object is ever asked
    // for its backtrace.
    // Fixing this remaining case of 4292742 requires some flavor of
    // escape analysis.  Leave that for the future.
    ciInstance* ex_obj = NULL;
    switch (reason) {
    case Deoptimization::Reason_null_check:
      ex_obj = env()->NullPointerException_instance();
      break;
    case Deoptimization::Reason_div0_check:
      ex_obj = env()->ArithmeticException_instance();
      break;
    case Deoptimization::Reason_range_check:
      ex_obj = env()->ArrayIndexOutOfBoundsException_instance();
      break;
    case Deoptimization::Reason_class_check:
      if (java_bc() == Bytecodes::_aastore) {
        ex_obj = env()->ArrayStoreException_instance();
      } else {
        ex_obj = env()->ClassCastException_instance();
      }
      break;
    }
    ... ...
}

OmitStackTraceInFastThrow和StackTraceInThrowable都默認為true。所以條件 (!StackTraceInThrowable || OmitStackTraceInFastThrow)為true,即JVM默認開啟了Fast Throw優化。通過實測發現在若干次(測試多次,發現每次次數不一樣,有可能是JVM內部是一個動態值)以后便不打印堆棧信息。

測試代碼如下:

 public static void main(String[] args)
    {
        String test = null;
        int i = 0;
        while (true)
        {
            try
            {
                test.length();
            }
            catch (Exception e)
            {
                System.out.println("次數為:" + i++ + ",堆棧長度為:" + e.getStackTrace().length);
                //log.error("StackTrace is ",e);
                if (e.getStackTrace().length == 0)
                {
                    log.error("no StackTrace is ", e);
                    break;
                }
            }
        }
    }

問題解決:

方法一:由於該優化是在JIT重新編譯后才起作用,因此起初拋出的異常還是有堆棧的,所以可以查看較舊的日志,尋找完整的堆棧信息
方法二:另一個解決辦法是禁用該優化,即強制要求每次都要拋出有堆棧的異常, 即-XX:-OmitStackTraceInFastThrow,便可禁用該優化了( 注意選項中的減號,加號則表示啟用)

1)在idea中添加啟動參數,如下圖:

 2)在生產環境中添加啟動參數,如:

local OMIT_STACKTRACE_OPTS="-XX:-OmitStackTraceInFastThrow"
nohup $JAVA_HOME/bin/java $OMIT_STACKTRACE_OPTS -jar $JAR_NAME >/dev/null 2>&1 &

注:

JVM只對幾個特定類型異常開啟了Fast Throw優化,這些異常包括:

NullPointerException
ArithmeticException
ArrayIndexOutOfBoundsException
ArrayStoreException
ClassCastException


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM