由Sun的javac制作的奇怪异常表条目

aio*_*obe 58 java jvm bytecode

鉴于此计划:

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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Sun javac(v 1.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
Run Code Online (Sandbox Code Playgroud)

使用以下异常表:

  Exception table:
   from   to  target type
     0     8     8   Class NullPointerException
     0    17    28   any
    28    29    28   any
Run Code Online (Sandbox Code Playgroud)


我的问题是:为什么它包含异常表中的最后一个条目?!

据我了解,它基本上说" 如果astore_2抛出异常,抓住它,并重试相同的指令 ".

即使使用空的try/catch/finally子句,也会产生这样的条目

try {} catch (NullPointerException npe) {} finally {}
Run Code Online (Sandbox Code Playgroud)

一些观察

  • Eclipse编译器不会产生任何此类异常表条目
  • JVM规范,不会记录任何运行时异常astore指令.
  • 我知道JVM抛出VirtualMachineError任何指令都是合法的.我猜这个特殊的条目可以防止任何这样的错误从该指令传播出去.

fer*_*olo 9

只有两种可能的解释:编译器包含一个错误,或者由于不明原因而放置了一种水印.

该条目肯定是假的,因为finally块本身抛出的任何异常必须将执行流发送到外部异常处理程序或最后阻塞,但永远不会"再次"运行相同的finally块.

此外,一个很好的证据表明它是一个错误/水印,事实上Eclipse(可能还有其他Java编译器)没有生成这样的条目,即便如此,Eclipse生成的类在Sun的JVM上运行良好.

也就是说,这篇文章很有意思,因为它似乎是有效的并且验证了类文件.如果我是JVM实现者,我会忽略该条目并填写Sun/Oracle的错误!

  • "*finally块本身抛出的任何异常都必须将执行流发送到外部异常处理程序*" - 不,如果你有一个内部catch块,则不行.此外,您将Java与字节码混合在一起:字节码程序应该实现要编译的Java程序的语义.它可以通过抛出/捕获各种疯狂异常来实现.编译程序没有"正确"或"错误"的方式.我怀疑这个条目是编译器中一些更通用的构造的副作用.也许这样的条目甚至会在一些罕见的极端情况下从eclipse编译器中发出. (3认同)

Dan*_*ruz 7

看看OpenJDK 7源代码,我冒昧地猜测最后一个28 29 28 any异常表条目的原因是因为处理astore字节码的代码(参见从第1871行开始的代码)java.lang.LinkageError如果来自操作数堆栈的弹出值是不是一个returnAddressreference类型(请参阅Java虚拟机规范ASTORE),他们希望这种错误情况下显示出来的堆栈跟踪.

如果操作数堆栈上存在错误的操作数类型,JVM将清除操作数堆栈(摆脱该坏操作数),将LinkageError 操作数堆叠放在操作数堆栈上,astore再次执行字节码,这次成功执行astore字节码使用JVM提供的LinkageError 对象引用.有关更多信息,请参阅athrow文档.

我非常怀疑扔的根源LinkageErrorastore处理由于复杂JSR/RET子程序引入字节码验证(OpenJDK的变化 6878713,69324967020373是最近的证据JSR的不断复杂,我敢肯定,太阳/ Oracle有我们在OpenJDK中没有看到的其他闭源测试.OpenJDK 7020373更改用于LinkageError验证/使测试结果无效.