我们知道,当程序发生异常时,会通过new调用异常的构造方法,在堆内存区域创建一个异常实例。而构造方法都是默认调用基类的Throwalbe的构造方法,下面我们看一下代码:
public Throwable() {
fillInStackTrace();
}
public Throwable(String message) {
fillInStackTrace();
detailMessage = message;
}
可以看到,基类的构造方法中都调用了 一个叫fillInStackTrace()的方法,而这个方法的作用,便是用来保存运行时栈的快照,以便在处理异常时打印出调用栈信息。
这里先解释一下运行时栈。jvm在执行代码时,会将代码的调用按顺序依次压入栈中,比如一般的action+service+dao的结构,栈底就是action的代码,中间是service,栈顶是dao,然后pop出来执行。中间不会任何停顿。
而当发生Exception时,需要将此时运行时栈hold住,并保存一份快照,这对性能的影响是非常大的。
另外,在进行异常处理时,一般为了便于查找异常的原因,程序员都会调用e.printStrackTrace()来打印异常信息。再来看一下代码:
public void printStackTrace() {
printStackTrace(System.err);
}
public void printStackTrace(PrintStream s) {
synchronized (s) {
s.println(this);
StackTraceElement[] trace = getOurStackTrace();
for (int i=0; i < trace.length; i++)
s.println("\tat " + trace[i]);
Throwable ourCause = getCause();
if (ourCause != null)
ourCause.printStackTraceAsCause(s, trace);
}
}
可以发现,实际上是将错误信息交给了标准的错误流打印出来,同样也会有性能影响。