JVM如何保证finally块的执行?

这个问题针对的是JVM如何能够保证finally块的执行 (假设JVM没有崩溃并且线程没有被中断或退出)。

在面试问题的提示下,我试图了解JVM如何确保即使在奇怪的情况下也能执行finally块…请考虑以下代码:

try{ int[] someArray = new int[10]; int invalid = someArray[10]; } catch(IndexOutOfBoundsException e){ throw new RuntimeException("Other Exception"); } finally{ //close open files or HTTP connections etc. } 

虽然这可能是一个奇怪的情况,但仍然保证执行finally块,尽管没有明确处理其他exception 。 JVM如何处理这样的情况?

我的想法:

根据我的理解并且到目前为止已经读过,当遇到未处理的exception时,控制从当前线程传递到我认为的那个线程的ThreadGroupThreadGroup中是否有一些条款可以检查最终需要执行的块? 我能想到的唯一另一件事就是finally块的地址可能存储在某个地方。 然后JVM在检测到exception时执行goto,并在finally块执行完毕后返回exception。

谁能澄清这个过程究竟是如何发生的?

编译这个小程序(我意识到我应该使用你的例子,但没有区别)

 public static void main(String[] args) { try { Float s = Float.parseFloat("0.0327f"); } finally { System.out.println("hello"); } } 

我用了

 >java -version java version "1.8.0-ea" // should be same for 7 Java(TM) SE Runtime Environment (build 1.8.0-ea-b118) Java HotSpot(TM) 64-Bit Server VM (build 25.0-b60, mixed mode) 

然后执行

 javac -v -c  

获取字节码。 你会看到类似的东西

 public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: ldc #2 // String 0.0327f 2: invokestatic #3 // Method java/lang/Float.parseFloat:(Ljava/lang/String;)F 5: invokestatic #4 // Method java/lang/Float.valueOf:(F)Ljava/lang/Float; 8: astore_1 9: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 12: ldc #6 // String hello 14: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 17: goto 31 20: astore_2 21: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 24: ldc #6 // String hello 26: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 29: aload_2 30: athrow 31: return Exception table: from to target type 0 9 20 any 20 21 20 any LineNumberTable: line 10: 0 line 12: 9 line 13: 17 line 12: 20 line 14: 31 StackMapTable: number_of_entries = 2 frame_type = 84 /* same_locals_1_stack_item */ stack = [ class java/lang/Throwable ] frame_type = 10 /* same */ 

你会注意到finally代码出现两次 ,一次在goto之前,一次之后。 您还会注意到Exception table ,它指定在某行发生exception时要转到哪个语句。

因此,如果语句0-9之间发生任何exception,请转到第20行并在goto之后执行finally的所有内容。 如果没有exception发生,执行finally ,然后在goto之后执行goto跳过finally

在所有情况下,您都将在finally块中执行代码。

其他exception未明确处理

使用finally块,将创建一个Exception table条目,该条目将处理任何类型的Throwable


这是字节码指令的列表。

我相信这个博客清楚地描述了内部:

如果方法定义了try-catch或try-finallyexception处理程序,那么将创建一个exception表。 它包含每个exception处理程序或finally块的信息,包括处理程序应用的范围,正在处理的exception类型以及处理程序代码的位置。

抛出exception时,JVM在当前方法中查找匹配的处理程序,如果没有找到,则方法突然弹出当前堆栈帧,并在调用方法(新的当前帧)中重新抛出exception。 如果在弹出所有帧之前未找到exception处理程序,则终止该线程。 如果在最后一个非守护程序线程中抛出exception,这也可能导致JVM本身终止,例如,如果该线程是主线程。

最后,exception处理程序匹配所有类型的exception,因此每当抛出exception时总是执行。 在没有抛出exception的情况下,仍然在方法结束时执行finally块,这是通过在执行return语句之前立即跳转到finally处理程序代码来实现的。