Java 类文件上的 ACC_SUPER 访问标志的用途是什么?
invokespecial
JVM 指令用于在创建新对象时调用初始化方法 (
)。指令的描述表明(但没有澄清)是否调用超类的构造函数或当前类的构造函数的决定取决于在指令中设置的 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< /code> 格式)
定义指出该标志是为了向后兼容旧编译器。然而,它与 Sun 较旧的 Java 虚拟机实现在设置时忽略该标志相矛盾。
该标志是否仍与 invokespecial
操作码一起使用?据我所知,它似乎没有任何目的,而且我找不到任何资源来表明它曾经有过目的。
谢谢。
The invokespecial
JVM instruction is used for calling initialisation methods (<init>
) when creating new objects. The description of the instruction suggests (but doesn't clarify) that the decision on whether to call the constructor of a superclass or a constructor of the current class depends on the state of the ACC_SUPER
flag set within the class
file.
From the Sun JVM Specification:
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
opcode definition)
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
format)
The definition states that the flag is for backward compatibility with old compilers. However it goes on to contradict with Sun's older Java virtual machine implementations ignore the flag if it is set.
Is the flag still used with the invokespecial
opcode? From what I can tell, it seems to hold no purpose and I can't find a resource to suggest it ever did.
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
引入 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 类
Container
和Component
是这样定义的:在 Java 1.1 中,类
Container
被更改为拥有自己的实现paint
的:现在,当您有
Container
的直接或间接子类调用super.paint(g)
并将其编译为1.0.2 它生成了Component.paint
的invokenonvirtual
指令,因为这是第一个具有此方法的父级。但是,如果您在也具有 Container.paint 的 JVM 上使用此编译类,它仍然会调用 Component.paint,这不是您所期望的。另一方面,当您编译 1.1 的类并在 1.0.2 JVM 上执行它时,它会抛出 AbstractMethodError ,或者对于那个时代的 VM 来说更有可能只是崩溃。为了避免崩溃,您必须编写
((Component)super).paint(g)
并使用 1.1 编译器对其进行编译,才能在任一虚拟机中获得所需的行为。这将设置 ACC_SUPER 但仍生成调用 Component.paint 的指令。 1.0.2 VM 会忽略 ACC_SUPER 并直接调用 Component.paint ,这很好,而 1.1 VM 会找到 ACC_SUPER 集,从而自行进行查找,这将使其调用 Container。 Paint 即使字节码方法引用是Component.paint
。您可以在 ikvm.net 博客上的这篇旧帖子。
ACC_SUPER was introduced to correct a problem with the invocation of super methods. The ACC_SUPER flag marks a class as compiled for the changed semantics of the opcode 183 instruction. It's purpose is similar to that of the class file version number as it allows the JVM to detect whether a class was compiled for the older or newer semantics of that instruction. Java 1.0.2 did not set and ignored ACC_SUPER while Java 1.1 and later always sets ACC_SUPER.
Before Java 1.1 the byte code instruction with opcode 183 that is now called
invokespecial
was calledinvokenonvirtual
and had a partially different specification. It was used whenever instance methods had to be invoked without a virtual method lookup. This was the case for private methods, instance initializers (constructors) and to implement method invocations onsuper
. But the latter case caused problems with evolving class libraries.A method reference in byte code (
CONSTANT_Methodref_info
) not only defines the name and the argument and return types of a method but also the class to which it belongs. Opcode 183 gets such a method reference parameter and was meant to directly invoke the referenced method from the specified class without further lookups. In the case of invocations onsuper
it was the compilers responsibility to resolve the closest super class that implements this method and generate a reference to it into the byte code.Since Java 1.1 it was changed to essentially ignore the class referenced in
CONSTANT_Methodref_info
and to instead do the lookup for the closest super method with the given method name and signature in the JVM. This is usually done now when the class gets loaded or right before the instruction is executed or JIT compiled the first time.Here is an example why this change was neccessary. In Java 1.0.2 the AWT classes
Container
andComponent
were defined this way:In Java 1.1 the class
Container
was changed to have its own implementation ofpaint
:Now when you had a direct or indirect subclass of
Container
that made a call onsuper.paint(g)
and compiled it for 1.0.2 it generated ainvokenonvirtual
instruction forComponent.paint
since this was the first parent that had this method. But if you used this compiled class on a JVM that also hadContainer.paint
it would still have calledComponent.paint
which is not what you would expect.On the other hand, when you compiled the class for 1.1 and executed it on a 1.0.2 JVM it would throw a AbstractMethodError or more likely for VMs of that era simply crash. To avoid the crash you had to write
((Component)super).paint(g)
and compile it with a 1.1 compiler to get the desired behaviour in either VM. This would set ACC_SUPER but still generate the instruction to callComponent.paint
. A 1.0.2 VM would ignore ACC_SUPER and go straight to invokeComponent.paint
which is fine while a 1.1 VM would find ACC_SUPER set and thus do the lookup itself which would make it invokeContainer.paint
even though the byte code method reference wasComponent.paint
.You can find more about this in this old post on the ikvm.net weblog.
自 Java 8 以来,
ACC_SUPER
标志没有执行任何操作。根据 JEP 草案:更好定义的 JVM类文件验证:
虽然我还没有找到一个简洁、易于链接的权威来源,但似乎该功能在 Java 8 之前已在 Java 7 安全更新中被删除。
The
ACC_SUPER
flag has not done anything since Java 8.Per JEP draft: Better-defined JVM class file validation:
While I haven't found a single concise easily-linkable authoritative source, it seems that the functionality was removed prior to Java 8, in a Java 7 security update.