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