JVM异常
在java中,异常分为显示异常和隐式异常两种。显示异常指java应用抛出的异常,程序遇到了无法执行的逻辑,通过throw抛出异常;隐式异常指虚拟机在执行过程中,碰到错误无法执行下去,不得不停止执行,自动抛出异常,比如对象执行方法,结果该对象为Null,那么会出现NPE。
关于异常的内容
为什么不能通过异常来处理业务逻辑
异常实例的构建过程十分复杂。在某个方法中生成了异常,一般我们会得到这个异常的栈轨迹,这个内容就是由JVM来生成的。JVM会从当前出现异常的方法开始,逐一向下访问所在线程的栈帧,并且记录下各种异常相关的信息,异常所在的类、方法、行。
JVM是如何捕捉异常的
在java文件编译后生成的字节码文件中,每个方法都带有一个异常表,异常表中含有from、to、target三种指针及不活的异常的类型,如果没有try、catch模块,那么捕获的异常类型为any。
当程序触发异常时,Java 虚拟机会从上至下遍历异常表,检查当前异常触发的索引是否在异常表中某个异常的监控范围内,如果在范围内,那么jvm会判断所抛出的异常与异常表查询的异常类型是否匹配,匹配的话那么跳转到该条异常所指定的字节。
如果遍历完了异常表没有查询到对应的异常,那么会弹出栈帧,将异常抛出到该方法的调用者,继续执行这个流程。
JVM中的finally
finally模块在java语言中是必须执行的,在编译的时候,通过将finally中代码块分别在try模块的最后和catch模块的最后都复制了一份,通过这样来保证finally的必定执行。如下面代码及class文件内容。
public class TestException {
private int tryValue;
private int catchValue;
private int finalValue;
public void test() {
try {
tryValue = 1;
} catch (Exception e) {
catchValue = 2;
} finally {
finalValue = 3;
}
}
/**
* 在索引为5和索引为19的位置可以看到,代码里并没有的为finalValue赋值的内容在这里出现了,
* 这是在编译的时候,将finally模块的内容添加过来保证finally的执行,执行结束后直接跳转到return.
* 那么在索引27的位置,依旧出现了finally的模块内容,这是因为可能在catch模块出现异常,
* 这个finally模块的内容是为了保证catch模块里异常后必定被执行(可看下面异常表)
* public void test();
* descriptor: ()V
* flags: ACC_PUBLIC
* Code:
* stack=2, locals=3, args_size=1
* 0: aload_0
* 1: iconst_1
* 2: putfield #2 // Field tryValue:I
* 5: aload_0
* 6: iconst_3
* 7: putfield #3 // Field finalValue:I
* 10: goto 35
* 13: astore_1
* 14: aload_0
* 15: iconst_2
* 16: putfield #5 // Field catchValue:I
* 19: aload_0
* 20: iconst_3
* 21: putfield #3 // Field finalValue:I
* 24: goto 35
* 27: astore_2
* 28: aload_0
* 29: iconst_3
* 30: putfield #3 // Field finalValue:I
* 33: aload_2
* 34: athrow
* 35: return
* // 方法中的异常表
* Exception table:
* from to target type
* 0 5 13 Class java/lang/Exception
* 0 5 27 any
* 13 19 27 any
*/
}
finally中return覆盖try中返回值
从字节码的角度来看finally中return是如何覆盖try中return的
public class TestException {
private int tryValue;
private int catchValue;
private int finalValue;
public int test() {
try {
tryValue = 1;
return tryValue;
} catch (Exception e) {
catchValue = 2;
} finally {
finalValue = 3;
}
return finalValue;
}
/**
* return在finally外版本
* public int test();
* descriptor: ()I
* flags: ACC_PUBLIC
* Code:
* stack=2, locals=3, args_size=1
* 0: aload_0
* 1: iconst_1
* 2: putfield #2 // Field tryValue:I
* 5: aload_0
* 6: getfield #2 // Field tryValue:I
* 9: istore_1 // 将tryValue写回return变量中
* 10: aload_0
* 11: iconst_3
* 12: putfield #3 // Field finalValue:I // 为finalValue赋值
* 15: iload_1 // 取return变量写入栈顶
* 16: ireturn // 返回栈顶变量
* 17: astore_1
* 18: aload_0
* 19: iconst_2
* 20: putfield #5 // Field catchValue:I
* 23: aload_0
* 24: iconst_3
* 25: putfield #3 // Field finalValue:I
* 28: goto 39
* 31: astore_2
* 32: aload_0
* 33: iconst_3
* 34: putfield #3 // Field finalValue:I
* 37: aload_2
* 38: athrow
* 39: aload_0
* 40: getfield #3 // Field finalValue:I // 取对象中finalValue
* 43: ireturn // 返回栈顶变量
* Exception table:
* from to target type
* 0 10 17 Class java/lang/Exception
* 0 10 31 any
* 17 23 31 any
*/
}
public class TestException {
private int tryValue;
private int catchValue;
private int finalValue;
public int test() {
try {
tryValue = 1;
return tryValue;
} catch (Exception e) {
catchValue = 2;
} finally {
finalValue = 3;
return finalValue;
}
}
/**
* return在finally模块里版本
* 最大的变化在索引12之后,获取到了对象中的finalValue,写入栈顶,然后返回,这里相当于覆盖了return变量里的值,因此try中的return失效了。
* public int test();
* descriptor: ()I
* flags: ACC_PUBLIC
* Code:
* stack=2, locals=3, args_size=1
* 0: aload_0
* 1: iconst_1
* 2: putfield #2 // Field tryValue:I
* 5: aload_0
* 6: getfield #2 // Field tryValue:I
* 9: istore_1
* 10: aload_0
* 11: iconst_3
* 12: putfield #3 // Field finalValue:I
* 15: aload_0
* 16: getfield #3 // Field finalValue:I
* 19: ireturn
* 20: astore_1
* 21: aload_0
* 22: iconst_2
* 23: putfield #5 // Field catchValue:I
* 26: aload_0
* 27: iconst_3
* 28: putfield #3 // Field finalValue:I
* 31: aload_0
* 32: getfield #3 // Field finalValue:I
* 35: ireturn
* 36: astore_2
* 37: aload_0
* 38: iconst_3
* 39: putfield #3 // Field finalValue:I
* 42: aload_0
* 43: getfield #3 // Field finalValue:I
* 46: ireturn
* Exception table:
* from to target type
* 0 10 20 Class java/lang/Exception
* 0 10 36 any
* 20 26 36 any
*/
}