Sun生成的奇怪异常表条目';s javac
鉴于该计划:Sun生成的奇怪异常表条目';s javac,java,jvm,bytecode,Java,Jvm,Bytecode,鉴于该计划: class Test { public static void main(String[] args) { try { throw new NullPointerException(); } catch (NullPointerException npe) { System.out.println("In catch"); } finally { Syst
class Test {
public static void main(String[] args) {
try {
throw new NullPointerException();
} catch (NullPointerException npe) {
System.out.println("In catch");
} finally {
System.out.println("In finally");
}
}
}
Sun的javac
(V1.6.0_24)生成以下字节码:
public static void main(java.lang.String[]);
// Instantiate / throw NPE
0: new #2; // class NullPointerException
3: dup
4: invokespecial #3; // Method NullPointerException."<init>":()V
7: athrow
// Start of catch clause
8: astore_1
9: getstatic #4; // Field System.out
12: ldc #5; // "In catch"
14: invokevirtual #6; // Method PrintStream.println
17: getstatic #4; // Field System.out
// Inlined finally block
20: ldc #7; // String In finally
22: invokevirtual #6; // Method PrintStream.println
25: goto 39
// Finally block
// store "incomming" exception(?)
28: astore_2
29: getstatic #4; // Field System.out
32: ldc #7; // "In finally"
34: invokevirtual #6; // Method PrintStream.println
// rethrow "incomming" exception
37: aload_2
38: athrow
39: return
我的问题是:到底为什么它会在例外表中包含最后一个条目强>
据我所知,它基本上说“如果
astore2
抛出异常,捕获它,然后重试同一条指令”
即使使用空的try/catch/finally子句(如
try {} catch (NullPointerException npe) {} finally {}
一些观察结果
- Eclipse编译器不生成任何此类异常表项
- JVM规范没有记录的任何运行时异常
- 我知道JVM对任何指令抛出
是合法的。我猜这个特殊的条目可以防止任何此类错误从该指令传播出去VirtualMachineError
这就是说,这篇文章很有趣,因为类文件似乎是有效的并经过验证的。如果我是一个JVM实现者,我会忽略这个条目并为Sun/Oracle填充一个bug 我的理解是,第二个异常表条目是编译器添加的隐式catch everything子句,用于覆盖正文或catch处理程序中抛出的任何异常/错误,第三个条目是隐式catch上的保护,以强制流通过最终执行。查看OpenJDK 7源代码,我冒昧地猜一下原因 最后一个
28任何异常表条目都是因为
处理astore
字节码(参见从第1871行开始)可以
如果弹出的值
来自操作数堆栈的不是返回地址
或引用
类型(请参阅
Java虚拟机规范),他们希望
要在堆栈跟踪上显示的错误条件
如果操作数堆栈上存在错误的操作数类型,JVM将
清除操作数堆栈(清除该错误操作数),放入LinkageError
在操作数堆栈上,再次执行astore
字节码
使用JVM提供的LinkageError
对象引用。有关更多信息,请参阅文档
我非常怀疑在运行期间抛出LinkageError
的根本原因
astore
处理是由于引入了字节码验证(OpenJDK更改)
,是
最近的证据表明,JSR的复杂性持续存在;我相信Sun/Oracle还有其他原因
我们在OpenJDK中没有看到的封闭源代码测试)。OpenJDK更改使用LinkageError
来验证/失效测试结果。我将把这篇文章作为一条评论发表,因为我还没有完全理解这个概念。关于这个主题有一个条目,关于为什么最后一个条目是在。显然,就Sun/Oracle编译器而言,VM规范中指定的编译器编译finally块的行为有点偏离了标准。最后一个异常表条目用于保护“生成的异常处理程序”。我没有弄清楚防护是如何操作的,以及为什么它应该以这种方式工作。“finally块本身引发的任何异常都必须将执行流发送到外部异常处理程序”——不,如果您有内部catch块,则不会。此外,您将Java与字节码混为一谈:字节码程序应该实现要编译的Java程序的语义。它可以通过抛出/捕获各种疯狂的异常来做到这一点。编译程序没有“正确”或“错误”的方法。我怀疑该条目是编译器中某些更一般的构造的副作用。在某些罕见的情况下,甚至可能会从eclipse编译器发出这样的条目。@aioobe虽然JVM可以成为其他语言而不是Java的目标,但这个问题明确显示了为JVM编译的Java程序。JVM可能允许异常由抛出它的代码来处理,但astore_2指令属于没有内部try的Java finally块,因此它抛出的任何异常实际上都必须将执行流发送到外部异常处理程序。“我怀疑这个条目是编译器中一些更一般的构造的副作用。”-我也这么认为,但这破坏了Java语言契约。这破坏了Java语言契约——不,它没有。例如,您可以这样解释:finally子句中的任何语句引发的任何异常都应该传播到子句之外。因此,如果finally子句中不存在任何语句,那么就不能从那里传播任何异常。因此,如果编译的块不包含任何语句,则禁止“compiled finally block”中的所有异常是完全合法的。@aioobe是的,但有问题的finally block确实包含语句!!!另一种解释为什么它是一个bug的方法是:如果某个JVM在astore_2函数上抛出一个VirtualMachineError(或另一个错误异常),它可能会进入一个无限循环,在使用javac编译时,它会消耗100%的CPU,甚至Java代码中没有任何循环。现在,如果使用Eclipse编译同一个类,VirtualMachineError将正确地将执行流发送到外部。这正是它实际上可以接受的原因
try {} catch (NullPointerException npe) {} finally {}