33

invokespecialJVM 指令用于在创建新对象时调用初始化方法 ( ) <init>。该指令的描述表明(但没有澄清)关于是调用超类的构造函数还是当前类的构造函数的决定取决于文件中ACC_SUPER设置的标志的状态class

来自 Sun JVM 规范:

接下来,除非以下所有条件都为真,否则选择已解析的方法进行调用:

  • 为当前类设置了 ACC_SUPER 标志(参见表 4.1,“类访问和属性修饰符”)。

--invokespecial操作码定义)

ACC_SUPER 标志的设置指示 Java 虚拟机要表达其 invokespecial 指令的两种替代语义中的哪一种;ACC_SUPER 标志的存在是为了向后兼容 Sun 的旧编译器为 Java 编程语言编译的代码。Java 虚拟机的所有新实现都应该实现本规范中记录的 invokespecial 语义。Java 虚拟机指令集的所有新编译器都应设置 ACC_SUPER 标志。Sun 的旧编译器生成了未设置 ACC_SUPER 的 ClassFile 标志。如果已设置,Sun 的旧 Java 虚拟机实现会忽略该标志。

--来源ClassFile格式)

该定义指出该标志是为了与旧编译器向后兼容。然而,它继续与Sun's older Java virtual machine implementations ignore the flag if it is set.

该标志是否仍与invokespecial操作码一起使用?据我所知,它似乎没有任何意义,而且我找不到任何资源来暗示它曾经做过。

谢谢。

4

1 回答 1

45

引入 ACC_SUPER 是为了纠正调用超级方法的问题。ACC_SUPER 标志将一个类标记为已针对操作码 183 指令的更改语义进行编译。它的目的类似于类文件版本号,因为它允许 JVM 检测一个类是针对该指令的旧语义还是新语义编译的。Java 1.0.2 未设置并忽略 ACC_SUPER,而 Java 1.1 及更高版本始终设置 ACC_SUPER。

在 Java 1.1 之前,现在调用的操作码为 183 的字节码指令invokespecial被调用invokenonvirtual并且具有部分不同的规范。每当必须在没有虚拟方法查找的情况下调用实例方法时都会使用它。私有方法、实例初始化程序(构造函数)和在super. 但是后一种情况导致了不断发展的类库的问题。

字节码 ( CONSTANT_Methodref_info) 中的方法引用不仅定义了方法的名称、参数和返回类型,还定义了它所属的类。操作码 183 获取这样一个方法引用参数,旨在直接从指定类调用引用的方法,而无需进一步查找。在调用super它的情况下,编译器负责解析实现此方法的最接近的超类,并将对它的引用生成到字节码中。

从 Java 1.1 开始,它被更改为基本上忽略其中引用的类CONSTANT_Methodref_info,而是在 JVM 中查找具有给定方法名称和签名的最接近的超级方法。这通常在类被加载时或在指令执行或第一次 JIT 编译之前完成。

这是一个示例,说明为什么需要进行此更改。在 Java 1.0.2 中,AWT 类ContainerComponent这样定义的:

class Component
{
    public void paint( Graphics g ) {}
}

class Container extends Component
{
    // inherits paint from Component but doesn't override it
}

在 Java 1.1 中,该类Container被更改为拥有自己的实现paint

class Container extends Component
{
    public void paint( Graphics g ) {/*...*/}
}

现在,当您有一个直接或间接的子类Container调用super.paint(g)并为 1.0.2 编译它时,它会生成一条invokenonvirtual指令,Component.paint因为这是第一个拥有此方法的父类。但是,如果您在同样拥有它的 JVM 上使用这个编译的类,Container.paint它仍然会调用Component.paint,这不是您所期望的。

另一方面,当您为 1.1 编译该类并在 1.0.2 JVM 上执行它时,它会抛出 AbstractMethodError 或者更有可能导致那个时代的 VM 崩溃。为了避免崩溃,您必须((Component)super).paint(g)使用 1.1 编译器编写和编译它,以在任一 VM 中获得所需的行为。这将设置 ACC_SUPER 但仍会生成调用指令Component.paint。1.0.2 VM 会忽略 ACC_SUPER 并直接调用Component.paint,这很好,而 1.1 VM 会找到 ACC_SUPER 集,因此会自行查找,Container.paint即使字节码方法引用是Component.paint.

您可以在 ikvm.net 博客上的这篇旧帖子中找到更多相关信息。

于 2012-01-21T04:07:33.953 回答