java - Java 类文件上的 ACC_SUPER 访问标志的目的是什么?

标签 java class jvm opcode

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

来自 Sun JVM 规范:

Next, the resolved method is selected for invocation unless all of the following conditions are true:

  • The ACC_SUPER flag (see Table 4.1, "Class access and property modifiers") is set for the current class.

-- Source ( invokespecial 操作码定义)

The setting of the ACC_SUPER flag indicates which of two alternative semantics for its invokespecial instruction the Java virtual machine is to express; the ACC_SUPER flag exists for backward compatibility for code compiled by Sun's older compilers for the Java programming language. All new implementations of the Java virtual machine should implement the semantics for invokespecial documented in this specification. All new compilers to the instruction set of the Java virtual machine should set the ACC_SUPER flag. Sun's older compilers generated ClassFile flags with ACC_SUPER unset. Sun's older Java virtual machine implementations ignore the flag if it is set.

-- Source (ClassFile 格式)

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

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

谢谢。

最佳答案

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

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

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

从 Java 1.1 开始,它被更改为基本上忽略 CONSTANT_Methodref_info 中引用的类,而是在 JVM 中查找具有给定方法名称和签名的最接近的 super 方法。这通常在类被加载时或在指令执行或第一次 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 时,它会生成一个 <Component.paint 的 code>invokenonvirtual 指令,因为这是具有此方法的第一个父级。但是如果你在一个也有 Container.paint 的 JVM 上使用这个编译的类,它仍然会调用 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。即使字节码方法引用是 Component.paint

您可以在 this old post on the ikvm.net weblog 中找到更多相关信息.

关于java - Java 类文件上的 ACC_SUPER 访问标志的目的是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8949933/

相关文章:

java - JVM 字节码指令 Tload_<n> 与 Tload

java - 执行 .bat 文件时启用按钮

java - 使用 Knime 定义 Oozie 工作流程

java - 如何在java中使用类型变量创建参数化类型?

python - 在 Python 中,类型是作为可调用类实现的吗?

java - 部署在 tomcat 中的 java 应用程序中的异常行为

java - 你应该总是用 Java 编写接口(interface)吗

java - 我应该使用哪个 JSObject?

java - 如何使用Java Reflect获取类级别注释值?

java - 如何在 64 位 JVM 上运行 TeamCity