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块仍然可以保证执行。JVM如何处理这种情况?

我的想法:

据我所知,到目前为止,当遇到未处理的异常时,控制权会从当前线程转移到该线程的ThreadGroupThreadGroup中是否有一些条款可以检查需要执行的finally块?我唯一能想到的可能是finally块的地址存储在某个地方。然后JVM在检测到异常时执行goto,并在finally块完成执行时返回到异常。

有人能澄清这个过程是如何发生的吗?

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

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 <fully qualified class name>

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

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,它指定在某行发生异常时要转到哪个语句。

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

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

未明确处理的其他异常

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


下面列出了字节码指令。

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

如果一个方法定义了try-catch或try-filly异常处理程序,则将创建一个异常表。这包含每个异常处理程序或finally块的信息,包括处理程序应用的范围,正在处理的异常类型已处理以及处理程序代码所在的位置。

当抛出异常时JVM在当前方法中查找匹配的处理程序(如果没有)发现该方法突然结束弹出当前堆栈帧,并且在调用方法中重新抛出异常(新的当前框架)。如果在所有帧之前未找到异常处理程序弹出,则线程终止。这也可能导致JVM如果在最后一个非后台进程中引发异常,则自身将终止线程,例如,如果线程是主线程。

最后,异常处理程序匹配所有类型的异常,因此每当抛出异常时总是执行。如果没有抛出异常finally块仍在方法,这是通过跳到finally处理程序代码来实现的紧接在执行return语句之前。

最新更新