事实上,每个throwable是java.lang.Throwable
的一个实例隐含在Java字节代码/ JVM的各个位置。即使任何处理器注定要可能代表Throwable
类型层次之外的东西,这种想法在今天的类文件必须包含异常处理方法StackMapTable
和StackMapTable
将参考Throwable作为实例的任何失败java.lang.Throwable
1。
即使旧类型推理验证,该处理程序重新抛出一个抛出隐含包含任何抛出是java.lang.Throwable
实例因为这是被允许抛出的唯一对象athrow
断言。
http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.athrow
的objectref必须reference
类型,并且必须引用的对象,则Throwable
类或的Throwable
一个子类的实例。
简短的回答:不,这是不可能有地方比java.lang.Throwable
(或子类)的实例其他的东西可以引发或捕获的情况。
我试图创建一个try-with-resource语句的最小示例来分析javac
的输出。结果清楚地表明,该结构是javac
如何在内部工作的人造物,但不能是故意的。
的例子是这样的:
public static void tryWithAuto() throws Exception {
try (AutoCloseable c=dummy()) {
bar();
}
}
private static AutoCloseable dummy() {
return null;
}
private static void bar() {
}
(我jdk1.8.0_20
编译)
我把异常处理程序表在生成的字节代码的开头,以便它更容易参照位置,同时看指令序列:
Exception table:
from to target type
17 23 26 Class java/lang/Throwable
6 9 44 Class java/lang/Throwable
6 9 49 any
58 64 67 Class java/lang/Throwable
44 50 49 any
我们指示:
开始很简单,使用两个局部变量,一个用于保存AutoCloseable
(索引0),另一个用于可能的throwable(索引1,用null
初始化)。调用dummy()
和bar()
,然后检查AutoCloseable
的null
以查看它是否必须关闭。
0: invokestatic #2 // Method dummy:()Ljava/lang/AutoCloseable;
3: astore_0
4: aconst_null
5: astore_1
6: invokestatic #3 // Method bar:()V
9: aload_0
10: ifnull 86
我们拿到这里如果AutoCloseable
不null
和第一奇怪的事情发生时,抛出这绝对是null
被检查null
13: aload_1
14: ifnull 35
下面的代码将关闭AutoCloseable
,由看守从上面的表中的第一个异常处理程序将调用addSuppressed
。由于在这一点上,变#1 null
这是死代码:
17: aload_0
18: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
23: goto 86
26: astore_2
27: aload_1
28: aload_2
29: invokevirtual #6 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
32: goto 86
注意,死代码的最后一个指令是goto 86
,跳转至return
因此,如果上面的代码中并没有死的代码吗,我们可能会开始想知道为什么在Throwable
上调用addSuppressed
之后会被忽略。
现在遵循如果变量#1是null
(总是阅读)执行的代码。它只是调用close
并分支到return
指令,不获取任何的异常,所以通过close()
抛出的异常传播给调用者:
35: aload_0
36: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
41: goto 86
现在我们进入第二个异常处理程序,覆盖了try
语句体,宣布赶上Throwable
,阅读所有例外。如预期的那样,它将Throwable
存储到变量#1中,但也将其存储到废弃变量#2中。然后它重新抛出Throwable
。
44: astore_2
45: aload_2
46: astore_1
47: aload_2
48: athrow
以下代码是的异常处理程序的目标。首先,它的目标是多余的任何异常处理程序,其覆盖范围与Throwable
处理程序相同,因此,如您所怀疑的,此处理程序不会执行任何操作。此外,它是第四个异常处理程序的目标,捕获任何东西并覆盖上面的异常处理程序,因此我们在稍后的一条指令中捕获指令#48的重新抛出的异常。为了让事情更有趣,异常处理程序不仅覆盖了上面的处理程序,在#50结束,排斥,甚至涵盖了自身的第一个指令:
49: astore_3
所以,第一件事就是要引入第三个变量来持有相同抛出。现在检查AutoCloseable
的null
。
50: aload_0
51: ifnull 84
现在变量#1的抛出被检查null
。只有在假设投掷项不存在Throwable
的情况下,它才可以是null
。但要注意,整个代码会在这种情况下,验证者拒绝为StackMapTable
声明所有变量和持有任何抛出操作数堆栈条目是分配兼容java.lang.Throwable
54: aload_1
55: ifnull 78
58: aload_0
59: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
64: goto 84
最后但并非最不重要的,我们有异常处理程序,它处理异常时存在的将会调用addSuppressed
并重新抛出主要异常的异常时由关闭抛出的异常。它引入了另一个局部变量,即使在适当的情况下也表示javac
indeed never uses swap
。
67: astore 4
69: aload_1
70: aload 4
72: invokevirtual #6 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
75: goto 84
因此,如果捕捉任何可能意味着比其他java.lang.Throwable
东西是不是这种情况下两个指令只调用。代码路径在#84处与常规情况结合。
78: aload_0
79: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
84: aload_3
85: athrow
86: return
因此,底线是,任何额外的异常处理程序负责的只有四个指令,#54,#55,#78和#79死代码,而甚至有更多的死码其他原因(#17 - #32),再加上一个奇怪的“抛出并捕获”(#44 - #48)代码,这也可能是任何不同于Throwable
的想法的人工产物。此外,一个异常处理程序的覆盖范围错误,可能与“Strange exception table entry produced by Sun's javac”为suggested in the comments有关。
作为边注,Eclipse将产生更直接的代码只服用60字节,而不是87的指令序列中,具有两个预期的异常处理程序只和三个局部变量,而不是5。在更紧凑的代码中,它处理了可能的情况,即由主体引发的异常可能与close
所抛出的异常相同,在这种情况下addSuppressed
一定不能被调用。 javac
生成的代码不关心这一点。
0: aconst_null
1: astore_0
2: aconst_null
3: astore_1
4: invokestatic #18 // Method dummy:()Ljava/lang/AutoCloseable;
7: astore_2
8: invokestatic #22 // Method bar:()V
11: aload_2
12: ifnull 59
15: aload_2
16: invokeinterface #25, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
21: goto 59
24: astore_0
25: aload_2
26: ifnull 35
29: aload_2
30: invokeinterface #25, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
35: aload_0
36: athrow
37: astore_1
38: aload_0
39: ifnonnull 47
42: aload_1
43: astore_0
44: goto 57
47: aload_0
48: aload_1
49: if_acmpeq 57
52: aload_0
53: aload_1
54: invokevirtual #30 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
57: aload_0
58: athrow
59: return
Exception table:
from to target type
8 11 24 any
4 37 37 any
请张贴完整的字节码。 – 2014-09-02 03:33:52
https://dl.dropboxusercontent.com/u/26793257/example.txt – 2014-09-02 04:13:31
这里是jimple代码(由soot生成): https://dl.dropboxusercontent.com/u/26793257/example。 jimple.txt 我在那里写了两条评论来指出相关的行。 – 2014-09-02 04:19:03