当使用 OpenJDK 8 中的 Java 编译器编译以下代码时,调用 foo()
通过 invokespecial
完成,但是当使用 OpenJDK 11 时,invokevirtual
被发射。
public class Invoke {
public void call() {
foo();
}
private void foo() {}
}
javap -v -p
的输出当javac
使用 1.8.0_282: public void call();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #2 // Method foo:()V
4: return
javap -v -p
的输出当javac
使用 11.0.10: public void call();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokevirtual #2 // Method foo:()V
4: return
我不明白为什么 invokevirtual
在这里使用,因为不能覆盖 foo()
.经过一番挖掘,似乎
invokevirtual
的目的关于私有(private)方法是允许嵌套类从外部类调用私有(private)方法。所以我尝试了下面的代码:public class Test{
public static void main(String[] args) {
// Build a Derived such that Derived.getValue()
// somewhat "exists".
System.out.println(new Derived().foo());
}
public static class Base {
public int foo() {
return getValue() + new Nested().getValueInNested();
}
private int getValue() {
return 24;
}
private class Nested {
public int getValueInNested() {
// This is getValue() from Base, but would
// invokevirtual call the version from Derived?
return getValue();
}
}
}
public static class Derived extends Base {
// Let's redefine getValue() to see if it is picked by the
// invokevirtual from getValueInNested().
private int getValue() {
return 100;
}
}
}
用 11 编译这段代码,我们可以在 javap
的输出中看到那invokevirtual
用于foo()
在 getValueInNested()
: public int foo();
descriptor: ()I
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
0: aload_0
// ** HERE **
1: invokevirtual #2 // Method getValue:()I
4: new #3 // class Test$Base$Nested
7: dup
8: aload_0
9: invokespecial #4 // Method Test$Base$Nested."<init>":(LTest$Base;)V
12: invokevirtual #5 // Method Test$Base$Nested.getValueInNested:()I
15: iadd
16: ireturn
public int getValueInNested();
descriptor: ()I
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #1 // Field this$0:LTest$Base;
// ** HERE **
4: invokevirtual #3 // Method Test$Base.getValue:()I
7: ireturn
所有这些都有点令人困惑,并提出了一些问题:invokevirtual
是用来调用私有(private)方法的吗?是否有用 invokespecial
替换它的用例?会不会等价? getValue()
在 Nested.getValueInNested()
不要从 Derived
中选择方法因为它是通过 invokevirtual
调用的? 最佳答案
这是作为 https://openjdk.java.net/jeps/181 的一部分完成的。 :基于嵌套的访问控制,这样 JVM 可以允许从嵌套类访问私有(private)方法。
在此更改之前,编译器必须在 Base
中生成一个受包保护的合成方法。嵌套类调用的类。该合成方法将依次调用 Base
中的私有(private)方法。类(class)。 Java 11 中的功能增强了 JVM 以允许编译器不必生成合成方法。
关于是否 invokevirtual
将调用 Derived
中的方法课,答案是否定的。私有(private)方法仍然不受 method selection 约束运行时类的(这从未改变过):
During execution of an
invokeinterface
orinvokevirtual
instruction, a method is selected with respect to (i) the run-time type of the object on the stack, and (ii) a method that was previously resolved by the instruction. The rules to select a method with respect to a class or interface C and a method mR are as follows:
- If mR is marked
ACC_PRIVATE
, then it is the selected method.
编辑:
基于评论“如果从方法所有者类调用私有(private)方法并使用invokevirtual如果从嵌套类调用,仍然使用invokespecial是否有效?”
正如 Holger 提到的,是的,它是有效的,但基于 JEP , 我猜决定切换到invokevirtual
为简单起见(虽然我无法证实,这只是一个猜测):
With the change to the access rules, and with suitable adjustments to byte code rules, we can allow simplified rules for generating invocation bytecodes:
- invokespecial for private nestmate constructors,
- invokevirtual for private non-interface, nestmate instance methods,
- invokeinterface for private interface, nestmate instance methods; and
- invokestatic for private nestmate, static methods
来自 JDK-8197445 : Implementation of JEP 181: Nest-Based Access Control 的另一个有趣的注释:
Traditionally,
invokespecial
is used to invokeprivate
members, thoughinvokevirtual
also has this capability. Rather than perturb the complex rules about supertypes enforced byinvokespecial
, we require invocations ofprivate
methods in a different class to useinvokevirtual
.
关于java - 为什么Java编译器11使用invokevirtual调用私有(private)方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67226178/