理解Exception,优化应用性能
- 1.从Exception往上介绍相关结构、代码
class Exception 里面没有什么新鲜东西,它继承自 class Throwable ,接下来我们看一下 Throwable 的结构,在它的构造函数中调用了 fillInStackTrace 这个函数。接下来我们看看这个函数干了些什么。
fillInStackTrace 函数的声明为
public synchronized native Throwable fillInStackTrace();
这是个 native 方法。
然后我们到jdk的代码里去找它的具体实现。
Java_java_lang_Throwable_fillInStackTrace(JNIEnv *env, jobject throwable) { JVM_FillInStackTrace(env, throwable); return throwable; } JVM_ENTRY(void, JVM_FillInStackTrace(JNIEnv *env, jobject receiver)) JVMWrapper("JVM_FillInStackTrace"); Handle exception(thread, JNIHandles::resolve_non_null(receiver)); java_lang_Throwable::fill_in_stack_trace(exception); JVM_END void java_lang_Throwable::fill_in_stack_trace(Handle throwable, TRAPS) { if (!StackTraceInThrowable) return; ResourceMark rm(THREAD); ………………………………………………. BacktraceBuilder bt(CHECK); ……………………………………………… for (frame fr = thread->last_frame(); max_depth != total_count;) { methodOop method = NULL; int bci = 0; ……………………………………………… bt.push(method, bci, CHECK); total_count++; } // Put completed stack trace into throwable object set_backtrace(throwable(), bt.backtrace()); }
上面的代码中,这一系列调用可以发现,当你 new 一个 exception 的时候, jvm 已经在 exception 里构建好了所有的 stacktrace( BacktraceBuilder bt ) ,这里花费的代价是可观的,试想一下,在web项目中,调用栈的深度可是很大的。因此,当你对 stacktrace 不感兴趣的时候,不需要这样的信息时,最好不要随便的 new exception 。
这里介绍一个常用的避免这种问题的相应的解决方法,即不需要stacktrace信息时,抛自己定义的特殊excepion。
自定义 XXXException ,覆盖掉 native 的那个函数 , 构造一个空的函数即可,具体实现如下。
XXXException extends Exception { public void synchronized fillInStackTrace(){} … }
然后throw exception的时候,抛自定义的XXXException就好了,这样会大大的提高效率,也节省了空间。
- 2.后记
当然做 getStackTrace() 的代价是蛮大的。曾经遇到一个案例,只需要 stacktrace 中的某个 trace ,却要通过 getStackTrace() 这个函数取到所有的 trace ,取其中的第 i 个,这样着实有些不划算。后来我们在 jdk 中给提供了一个接口 StackTraceElement XXXUtils::getStackTraceElement(int index, Throwable t) 便可以达到这个目的,节约了不小的时间开销,也省了内存。