由 Sun 的 javac 生成的奇怪的异常表条目

2022-08-31 17:40:26

给定此程序:

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 的 (v 1.6.0_24) 产生以下字节码:javac

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

但有以下例外表:

  Exception table:
   from   to  target type
     0     8     8   Class NullPointerException
     0    17    28   any
    28    29    28   any


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

据我所知,它基本上是说“如果astore_2抛出异常,抓住它,然后重试相同的指令”。

即使使用空的 try/catch/ finally 子句(例如

try {} catch (NullPointerException npe) {} finally {}

二. 一些意见

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

答案 1

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

该条目肯定是假的,因为最终块本身引发的任何异常都必须将执行流发送到外部异常处理程序或最终块,但永远不会“再次运行”相同的最终块。

此外,一个很好的证据表明它是一个错误/水印,即Eclipse(也许还有其他Java编译器)没有生成这样的条目,即使这样Eclipse生成的类在Sun的JVM上也能正常工作。

也就是说,这篇文章很有趣,因为看起来类文件是有效和经过验证的。如果我是JVM实现者,我会忽略该条目并为Sun / Oracle填充一个错误!


答案 2

查看 OpenJDK 7 源代码,我冒昧地猜测最后一个异常表条目的原因是,如果操作数堆栈中的弹出值不是 or 类型(请参阅 astore 的 Java 虚拟机规范),并且他们希望此错误条件显示在堆栈跟踪上,则处理字节码的代码(请参阅从第 1871 行开始的代码)可以引发 java.lang.LinkageError 异常。28 29 28 anyastorereturnAddressreference

如果操作数堆栈上有错误的操作数类型,JVM 将清除操作数堆栈(摆脱该错误的操作数),在操作数堆栈上放置 a,然后再次执行字节码,这次使用 JVM 提供的对象引用成功执行字节码。有关详细信息,请参阅 athrow 文档。LinkageErrorastoreastoreLinkageError

我非常怀疑在处理过程中抛出a的根本原因是由于JSR / RET子例程引入字节码验证的复杂性(OpenJDK更改687871369324967020373是持续复杂性的最新证据;我敢肯定Sun/Oracle还有其他我们在OpenJDK中看不到的闭源测试。OpenJDK 7020373更改用于验证/使测试结果无效。LinkageErrorastoreJSRLinkageError


推荐