拋異常的最大開銷是異常棧的構(gòu)建過程,如果你的程序調(diào)用很深,特別是用了第三方開源框架,這個(gè)開銷是不容忽視的
開銷在哪
查看jdk源碼
/**
* Constructs a new throwable with the specified cause and a detail
* message of {@code (cause==null ? null : cause.toString())} (which
* typically contains the class and detail message of {@code cause}).
* This constructor is useful for throwables that are little more than
* wrappers for other throwables (for example, {@link
* java.security.PrivilegedActionException}).
*
* <p>The {@link #fillInStackTrace()} method is called to initialize
* the stack trace data in the newly created throwable.
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public Throwable(Throwable cause) {
fillInStackTrace();
detailMessage = (cause==null ? null : cause.toString());
this.cause = cause;
}
主要的性能瓶頸在fillInStackTrace,這是一個(gè)native方法.會(huì)構(gòu)建整個(gè)異常棧. 方法簽名如下
/**
* Fills in the execution stack trace. This method records within this
* {@code Throwable} object information about the current state of
* the stack frames for the current thread.
*
* <p>If the stack trace of this {@code Throwable} {@linkplain
* Throwable#Throwable(String, Throwable, boolean, boolean) is not
* writable}, calling this method has no effect.
*
* @return a reference to this {@code Throwable} instance.
* @see java.lang.Throwable#printStackTrace()
*/
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
private native Throwable fillInStackTrace(int dummy);
如何解決
- 創(chuàng)建異常類的時(shí)候重寫fillInStackTrace方法.java7原生支持
protected Throwable(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
if (writableStackTrace) {
fillInStackTrace();
} else {
stackTrace = null;
}
detailMessage = message;
this.cause = cause;
if (!enableSuppression)
suppressedExceptions = null;
}
- 去掉異常.現(xiàn)在很多業(yè)務(wù)系統(tǒng)用異常實(shí)現(xiàn)程序正常業(yè)務(wù)邏輯.這個(gè)對(duì)性能影響比較大,尤其是并發(fā)比較大的時(shí)候.
尋找異常
有時(shí)候你無法知道那個(gè)異常拋的最多,有些三方包 自己throw Exception 但自己又catch住.
- brace 跟蹤Exception <init> 對(duì)異常棧,匯總
- perf top去看下us的開銷,如果_ZN19java_lang_Throwable19fill_in_stack_traceE6HandleP6Thread這個(gè)排名很靠前,那就好好檢查下.
討論
用異常實(shí)現(xiàn)正常的業(yè)務(wù)流程有以下優(yōu)點(diǎn)
- 代碼比較精煉.增強(qiáng)代碼可讀性.可以不用對(duì)服務(wù)方法設(shè)計(jì)統(tǒng)一的返回值
- 可以使用切面技術(shù)(攔截器 異常處理器) 對(duì)異常做統(tǒng)一的監(jiān)控 和處理.
缺點(diǎn):性能
改進(jìn):
- 不構(gòu)建異常堆棧,而是保存失敗原因FailCause或者錯(cuò)誤碼
- 重寫fillInStackTrace方法進(jìn)行noop
參考
知乎上有異常作為業(yè)務(wù)流程控制的討論.請移步
https://www.zhihu.com/question/21405047