[英]Why does the Java compiler 11 use invokevirtual to call private methods?
When compiling the code below with the Java compiler from OpenJDK 8, the call to foo()
is done via an invokespecial
, but when OpenJDK 11 is used, an invokevirtual
is emitted.当使用 OpenJDK 8 中的 Java 编译器编译下面的代码时,对
foo()
的调用是通过invokespecial
完成的,但是当使用 OpenJDK 11 时,会发出一个invokevirtual
。
public class Invoke {
public void call() {
foo();
}
private void foo() {}
}
Output of javap -v -p
when javac
1.8.0_282 is used:使用
javac
1.8.0_282 时javap -v -p
的 Output :
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
Output of javap -v -p
when javac
11.0.10 is used:使用
javac
11.0.10 时javap -v -p
的 Output :
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
I don't get why invokevirtual
is used here since there cannot be an override of foo()
.我不明白为什么在这里使用
invokevirtual
因为不能覆盖foo()
。
After digging a bit it seems that the purpose of invokevirtual
on private methods is to allow nested classes to call private methods from the outer class.经过一番挖掘,似乎在私有方法上调用虚拟的目的是允许嵌套类从外部
invokevirtual
调用私有方法。 So I tried the code below:所以我尝试了下面的代码:
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;
}
}
}
Compiling this code with 11, we can see in the output of javap
that invokevirtual
is used both in foo()
and in getValueInNested()
:用 11 编译这段代码,我们可以在
javap
的invokevirtual
中看到,在foo()
和getValueInNested()
中都使用了 invokevirtual:
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
All of this is a bit confusing and raises some questions:所有这些都有点令人困惑,并提出了一些问题:
invokevirtual
is used to call private methods?invokevirtual
用来调用私有方法? Is there a use case where replacing it by an invokespecial
would not be equivalent?invokespecial
替换它的用例不等效?getValue()
in Nested.getValueInNested()
not pick the method from Derived
since it is called via invokevirtual
? Nested.getValueInNested()
中对getValue()
的调用如何不从Derived
中选择方法,因为它是通过invokevirtual
调用的? This was done as part of https://openjdk.java.net/jeps/181 : Nest-Based Access Control, so that the JVM can allow access to private methods from nested classes.这是作为https://openjdk.java.net/jeps/181的一部分完成的:基于嵌套的访问控制,因此 JVM 可以允许从嵌套类访问私有方法。
Before that change, the compiler would have to generate a package-protected synthetic method in the Base
class, which the nested class invokes.在此更改之前,编译器必须在嵌套 class 调用的
Base
class 中生成一个受包保护的合成方法。 That synthetic method would in turn call the private method in the Base
class.该合成方法将依次调用
Base
class 中的私有方法。 The feature in Java 11 enhances the JVM to allow that without the compiler having to generate a synthetic method. Java 11 中的功能增强了 JVM 以允许编译器不必生成合成方法。
Concerning the point on whether invokevirtual
will call the method in the Derived
class, the answer is no.关于
invokevirtual
是否会调用Derived
class 中的方法这一点,答案是否定的。 The private method is still not subject to method selection of the runtime class (this has never changed):私有方法仍然不受运行时 class 的方法选择的影响(这从未改变过):
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.在执行
invokeinterface
或invokevirtual
指令期间,根据(i)堆栈上object的运行时类型和(ii)先前由指令解析的方法来选择方法。 The rules to select a method with respect to a class or interface C and a method m R are as follows:select 一个方法关于 class 或接口 C 和一个方法 m ZE1E1D3D405731283DEECEE6的规则如下:
- If m R is marked
ACC_PRIVATE
, then it is the selected method.如果 m R标记为
ACC_PRIVATE
,则它是选定的方法。
EDIT:编辑:
Based on the comment "Would it be valid to still use invokespecial if the private method is called from the method owner class and use invokevirtual if it is called from a nested class?"基于评论“如果私有方法是从方法所有者 class 调用的,并且如果从嵌套的 class 调用它,则使用 invokevirtual 是否仍然有效?”
As Holger mentioned, yes it is valid, but based on the JEP , I'm guessing a decision was made to switch to invokevirtual
for simplicity (I cannot confirm this though, it's just a guess):正如 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,
invokespecial 用于私有的 nestmate 构造函数,
- invokevirtual for private non-interface, nestmate instance methods,
invokevirtual 用于私有非接口,nestmate 实例方法,
- invokeinterface for private interface, nestmate instance methods;
私有接口的invokeinterface,nestmate实例方法; and
和
- invokestatic for private nestmate, static methods
私人巢友的invokestatic,static方法
Another interesting note from JDK-8197445: Implementation of JEP 181: Nest-Based Access Control :来自JDK-8197445 的另一个有趣的注释:JEP 181 的实现:基于嵌套的访问控制:
Traditionally,
invokespecial
is used to invokeprivate
members, thoughinvokevirtual
also has this capability.传统上,
invokespecial
用于调用private
成员,尽管invokevirtual
也具有此功能。 Rather than perturb the complex rules about supertypes enforced byinvokespecial
, we require invocations ofprivate
methods in a different class to useinvokevirtual
.我们不需要在不同的 class 中调用
private
方法来使用invokevirtual
,而不是扰乱由invokespecial
强制执行的有关超类型的复杂规则。
Thought it would be proper to add some more info to the answer already provided and accepted, although it isn't strictly necessary, it may help broaden the understanding, hence it's within SO users best interest.认为在已经提供和接受的答案中添加更多信息是合适的,尽管这不是绝对必要的,但它可能有助于扩大理解,因此它符合 SO 用户的最大利益。
In earlier versions, before Java 11, as already pointed out by @ma in the accepted answer, the compiler would need to create bridge methods to allow classes to access each other's private members in such conditions.在早期版本中,在 Java 11 之前,正如@ma在接受的答案中已经指出的那样,编译器需要创建桥接方法以允许类在这种情况下访问彼此的私有成员。 These accessibility-broadening bridge methods gets called in the execution context, where the compiler inserts code into a running program.
这些可访问性扩大的桥接方法在执行上下文中被调用,编译器将代码插入到正在运行的程序中。
Doing this increases both the size of the deployed applications and increases the complexity, plus it makes it harder to understand what is going on behind the scenes.这样做会增加已部署应用程序的大小并增加复杂性,而且更难理解幕后发生的事情。
Java 11 introduced the concept of nest-based access control . Java 11引入了基于嵌套的访问控制的概念。 Along with the notion of nestmates and the associated access rules within the JVM, this allows classes and interfaces to be nested within each other.
除了嵌套对象的概念和JVM中的相关访问规则外,这还允许类和接口相互嵌套。
Nested types can be private fields, methods, and constructors.嵌套类型可以是私有字段、方法和构造函数。
Using the updated reflection API you can now query for information about the nest-based access control functionality.使用更新的反射 API 您现在可以查询有关基于嵌套的访问控制功能的信息。
Some New Goodness in Java 11 Java 11 中的一些新优点
The getNestHost()
method is used to get the name of nest host and the isNestmateOf()
method can be used to check whether a class is a nestmate . getNestHost()
方法用于获取巢主机的名称, isNestmateOf()
方法可用于检查class是否为巢友。 Also, getNestMembers()
method returns an array of nest members.此外,
getNestMembers()
方法返回一个嵌套成员数组。
Here is a link to a generic example, courtesy of Baeldung.com, Nest Based Access Control that makes the benefits stand out pretty well IMHO.这是一个通用示例的链接,由 Baeldung.com 提供,基于巢的访问控制使得好处非常突出恕我直言。
Notice that there isn't a compiler generated bridging method in the disassembled code.请注意,反汇编代码中没有编译器生成的桥接方法。 Also, the Inner class can now make a direct call to the outerPrivate() method in the example linked to above.
此外,在上面链接的示例中,内部 class 现在可以直接调用 outerPrivate() 方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.