简体   繁体   English

Java编译器11为什么使用invokevirtual调用私有方法?

[英]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 编译这段代码,我们可以在javapinvokevirtual中看到,在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:所有这些都有点令人困惑,并提出了一些问题:

  • Why invokevirtual is used to call private methods?为什么invokevirtual用来调用私有方法? Is there a use case where replacing it by an invokespecial would not be equivalent?是否存在用invokespecial替换它的用例不等效?
  • How does the call to 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 or invokevirtual 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.在执行invokeinterfaceinvokevirtual指令期间,根据(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的规则如下:

  1. 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 invoke private members, though invokevirtual also has this capability.传统上, invokespecial用于调用private成员,尽管invokevirtual也具有此功能。 Rather than perturb the complex rules about supertypes enforced by invokespecial , we require invocations of private methods in a different class to use invokevirtual .我们不需要在不同的 class 中调用private方法来使用invokevirtual ,而不是扰乱由invokespecial强制执行的有关超类型的复杂规则。

Top-Up To an already good answer充值到一个已经很好的答案

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 用户的最大利益。

Nest-Based Access Control基于嵌套的访问控制

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.

相关问题 Java字节码操作&#39;invokevirtual&#39;不保持Object继承的方法的一致性 - Java bytecode operation 'invokevirtual' does not keep consistency for the methods inherited by Object 为什么使用 invokevirtual 操作码调用 java 中的最终方法 - Why final methods in java are invoked using invokevirtual opcode 为什么Java编译器为package-private超类型中定义的公共方法添加可见性桥接方法? - Why does the Java compiler add visibility bridge methods for public methods defined in package-private super types? 为什么Java的invokevirtual需要解析被调用方法的编译时类? - Why does Java's invokevirtual need to resolve the called method's compile-time class? 为什么 java 编译器选择使用 char[] - why does the java compiler choose to use char[] 为什么 Java 即时编译器继续重新编译相同的方法并使方法不可租用 - Why does Java Just in Time Compiler continue to recompile same methods and make methods non-rentrant Java 11 编译器返回“包不存在” - Java 11 compiler returns "package does not exist" 为什么java可以使用private来定义对象? 这是什么意思? - why java can use private to define the object ? what does it mean? java编译器如何知道继承的方法? - How does the java compiler know of inherited methods? Java接口 - 为什么没有私有方法? - Java Interfaces — why not have private methods?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM