文章目录

  • invokespecial
  • invokevirtual
  • invokestatic
  • invokeinterface
  • invokedynamic



  java里的五大invoke指令分别是invokespecial、invokevirtual、invokestatic、invokeinterface、invokedynamic。


  静态方法的调用,生成的是invokestatic指令。


  接口方法的调用,生成的是invokeinterface指令。


  其他的方法,一般生成invokevirtual指令,尽管final方法不可能被继承覆盖重写,但还是生成invokevirtual指令。


  调用构造器是生成invokespecial指令,此外super关键字方法调用,也生成invokespecial指令。


  最复杂的当属于invokedynamic指令了,其目的是实现动态类型。众所周知,java是一种静态类型语言。举个例子,python可以这样写代码:

class A:
    def print(self):
        print('A')


class B:
    def print(self):
        print('B')


def f(x):
    x.print()


if __name__ == '__main__':
    # 取命令行参数
    f(A())
    f(B())

  如果换成java,这两个类就必须实现同一个接口。但是现在java也可以了,JDK7引入的invokedynamic指令就可以实现这种动态类型,但是JDK7的java语法并不支持,只有JVM上动态语言比如closure、kotlin、Scala等语言提供了支持。但是到了JDK8,lambda表达式的出现使得java编译器可以生成invokedynamic指令。这个指令底层依靠methodhandle来实现,其实现原理超级复杂。但简而言之,就是把callsite传给invokedynamic指令,从call site中找到bootstrap method来调用真正的方法。

invokespecial

  我举个例子:

package com.youngthing.matrix.test.image;

/**
 * 11/22/2022 9:35 AM 创建
 *
 * @author 花书粉丝
 */
public final class InvokeSpecialTest {

    public void test() {
        super.hashCode();
    }
}

  使用javap -c反编译后的结果如下:

Compiled from "InvokeSpecialTest.java"
public final class com.youngthing.matrix.test.image.InvokeSpecialTest {
  public com.youngthing.matrix.test.image.InvokeSpecialTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void test();
    Code:
       0: aload_0
       1: invokespecial #2                  // Method java/lang/Object.hashCode:()I
       4: pop
       5: return
}

  反编译里,#2代表常量池里的第二条,这里代表java/lang/Object.hashCode:()I这个方法,I代表返回值为int。

invokevirtual

  私有方法的调用和其他方法的调用一样,都是invokevirtual,我举个例子:

package com.youngthing.matrix.test.image;

/**
 * 11/22/2022 9:35 AM 创建
 *
 * @author 花书粉丝
 */
public final class InvokeVirtualTest {

    private void print() {
        System.out.println("test");
    }
    public void test() {
        print();
    }
}

  反编译如下:

Compiled from "InvokeVirtualTest.java"
public final class com.youngthing.matrix.test.image.InvokeVirtualTest {
  public com.youngthing.matrix.test.image.InvokeVirtualTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void test();
    Code:
       0: aload_0
       1: invokevirtual #5                  // Method print:()V
       4: return
}

invokestatic

  我举个例子:

package com.youngthing.matrix.test.image;

/**
 * 11/22/2022 9:35 AM 创建
 *
 * @author 花书粉丝
 */
public final class InvokeStaticTest {

    public void test() {
        System.currentTimeMillis();
    }
}

  编译结果如下:

Compiled from "InvokeStaticTest.java"
public final class com.youngthing.matrix.test.image.InvokeStaticTest {
  public com.youngthing.matrix.test.image.InvokeStaticTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void test();
    Code:
       0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
       3: pop2
       4: return
}

  需要注意的是在这里,方法调用前没有任何load指令。因为System.currentTimeMillis()这个方法是静态方法,又没有任何方法参数,所以不需要加载任何变量。而非静态方法,哪怕没有参数,也要加载对象本身的。

invokeinterface

  我就以常见的List举例吧:

package com.youngthing.matrix.test.image;

import java.util.ArrayList;
import java.util.List;

/**
 * 11/22/2022 9:35 AM 创建
 *
 * @author 花书粉丝
 */
public final class InvokeInterfaceTest {

    public void test() {
        final List<Integer> list = new ArrayList<>();
        list.add(1);
    }
}

  反编译的结果很容易看到:

Compiled from "InvokeInterfaceTest.java"
public final class com.youngthing.matrix.test.image.InvokeInterfaceTest {
  public com.youngthing.matrix.test.image.InvokeInterfaceTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void test();
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
       7: astore_1
       8: aload_1
       9: iconst_1
      10: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      13: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      18: pop
      19: return
}

invokedynamic

  最后一个比较复杂了,为此,我写了一个简单的lambda表达式:

package com.youngthing.matrix.test.image;

import java.util.ArrayList;
import java.util.List;

/**
 * 11/22/2022 9:35 AM 创建
 *
 * @author 花书粉丝
 */
public final class InvokeDynamicTest {

    public void test() {
        final List<Integer> list = new ArrayList<>();
        list.stream().map(x->x++);
    }
}

  反编译后的指令里可以看到invokedynamic:

Compiled from "InvokeDynamicTest.java"
public final class com.youngthing.matrix.test.image.InvokeDynamicTest {
  public com.youngthing.matrix.test.image.InvokeDynamicTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void test();
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
       7: astore_1
       8: aload_1
       9: invokeinterface #4,  1            // InterfaceMethod java/util/List.stream:()Ljava/util/stream/Stream;
      14: invokedynamic #5,  0              // InvokeDynamic #0:apply:()Ljava/util/function/Function;
      19: invokeinterface #6,  2            // InterfaceMethod java/util/stream/Stream.map:(Ljava/util/function/Function;)Ljava/util/stream/Stream;
      24: pop
      25: return
}