JVM如何处理异常?
exception table
概念
方法的异常表
from :可能发生异常的起始点
to :可能发生异常的结束点
target :上述from和to之前发生异常后的异常处理者的位置
type :异常处理者处理的异常的类信息
什么时候被使用?
异常发生的时候
如何被使用?
1.JVM会在当前出现异常的方法中,查找异常表,是否有合适的处理者来处理
2.如果当前方法异常表不为空,并且异常符合处理者的from和to节点,并且type也匹配,则JVM调用位于target的调用者来处理。
3.如果上一条未找到合理的处理者,则继续查找异常表中的剩余条目
4.如果当前方法的异常表无法处理,则向上查找(弹栈处理)刚刚调用该方法的调用处,并重复上面的操作。
5.如果所有的栈帧被弹出,仍然没有处理,则抛给当前的Thread,Thread则会终止。
6.如果当前Thread为最后一个非守护线程,且未处理异常,则会导致JVM终止运行。
try...catch示例
public static void simpleTryCatch() { try { testNPE(); } catch (Exception e) { e.getMessage(); } } 字节码: public static void simpleTryCatch(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=1, args_size=0 0: invokestatic #3 // Method testNPE:()V 3: goto 12 6: astore_0 7: aload_0 8: invokevirtual #5 // Method java/lang/Exception.getMessage:()Ljava/lang/String; 11: pop 12: return Exception table: from to target type 0 3 6 Class java/lang/Exception
try...catch...finally示例
public static void simpleTryCatch() { try { testNPE(); } catch (Exception e) { e.getMessage(); }finally { System.out.println("finally"); } } 字节码: public static void simpleTryCatch(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=0 0: invokestatic #3 // Method testNPE:()V 3: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 6: ldc #5 // String finally 8: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 11: goto 42 14: astore_0 15: aload_0 16: invokevirtual #8 // Method java/lang/Exception.getMessage:()Ljava/lang/String; 19: pop 20: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 23: ldc #5 // String finally 25: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 28: goto 42 31: astore_1 32: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 35: ldc #5 // String finally 37: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 40: aload_1 41: athrow 42: return Exception table: from to target type 0 3 14 Class java/lang/Exception 0 3 31 any 14 20 31 any
异常表中,有三条数据,而我们仅仅捕获了一个Exception, 异常表的后两个item的type为any; 上面的三条异常表item的意思为:
如果0到3之间,发生了Exception类型的异常,调用14位置的异常处理者。
如果0到3之间,无论发生什么异常,都调用31位置的处理者
如果14到20之间(即catch部分),不论发生什么异常,都调用31位置的处理者。
finally为什么总是被执行?
finally逻辑 被提取到 try、catch位置
public static void simpleTryCatch() { try { testNPE(); } catch (Exception e) { e.getMessage(); }finally { System.out.println("finally"); } }
public static void simpleTryCatch(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=0 0: invokestatic #3 // Method testNPE:()V 3: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 6: ldc #5 // String finally 8: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 11: goto 42 // try提取finally逻辑,如果无异常,goto 42,执行结束 14: astore_0 15: aload_0 16: invokevirtual #8 // Method java/lang/Exception.getMessage:()Ljava/lang/String; 19: pop 20: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 23: ldc #5 // String finally 25: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 28: goto 42 // catch提取finally逻辑,如果无异常,goto 42,执行结束 31: astore_1 32: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 35: ldc #5 // String finally 37: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 40: aload_1 41: athrow // 如果发生异常,未被处理,执行完finally逻辑,将异常抛给调用方 42: return Exception table: from to target type 0 3 14 Class java/lang/Exception 0 3 31 any 14 20 31 any
return和finally的问题
在try、catch中使用return
finally都会正常被执行
public static String simpleTryCatch() { try { testNPE(); return "ok"; } catch (Exception e) { e.getMessage(); return "catch"; }finally { System.out.println("finally"); } } 字节码: public static java.lang.String simpleTryCatch(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=0 0: invokestatic #5 // Method testNPE:()V 3: ldc #6 // String ok 5: astore_0 6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 9: ldc #7 // String finally try块的finally逻辑 11: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 14: aload_0 15: areturn 16: astore_0 17: aload_0 18: invokevirtual #9 // Method java/lang/Exception.getMessage:()Ljava/lang/String; 21: pop 22: ldc #10 // String catch 24: astore_1 25: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 28: ldc #7 // String finally catch块的finally逻辑 30: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 33: aload_1 34: areturn 35: astore_2 36: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 39: ldc #7 // String finally 41: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 44: aload_2 45: athrow Exception table: from to target type 0 6 16 Class java/lang/Exception 0 6 35 any 16 25 35 any
在finally中使用return(禁止)
finally中使用return,finally逻辑执行完,执行执行return;
public static String simpleTryCatch() { try { testNPE(); return "ok"; } catch (Exception e) { e.getMessage(); return "catch"; }finally { System.out.println("finally"); return "finally"; } } 字节码: public static java.lang.String simpleTryCatch(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=0 0: invokestatic #5 // Method testNPE:()V 3: ldc #6 // String ok 5: astore_0 6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 9: ldc #7 // String finally 11: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 14: ldc #7 // String finally 执行完finally逻辑,执行return 16: areturn 17: astore_0 18: aload_0 19: invokevirtual #9 // Method java/lang/Exception.getMessage:()Ljava/lang/String; 22: pop 23: ldc #10 // String catch 25: astore_1 26: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 29: ldc #7 // String finally 31: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 34: ldc #7 // String finally 执行完finally逻辑,执行return 36: areturn 37: astore_2 38: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 41: ldc #7 // String finally 43: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 46: ldc #7 // String finally 执行完finally逻辑,执行return 48: areturn Exception table: from to target type 0 6 17 Class java/lang/Exception 0 6 37 any 17 26 37 any