Java中的invokedynamic和lambda表达式

Posted by RussXia on July 23, 2019

Java中的invokedynamic和lambda表达式

关于invokedynamic和其他四种字节码方法调用指令

在Java7中,JVM新增了invokedynamic指令。在此之前,JVM就已经提供了四种不同的字节码方法调用指令:

  • invokevirtual——对实例方法的标准分派
  • invokestatic——用于分派静态方法
  • invokeinterface——用于通过接口进行方法调用的分派
  • invokespecial——当需要进行非虚(也就是“精确”)分派时会用到,调用实例构造方法(方法),私有方法,父类继承方法。

下面是关于这四种方法分派方式的demo。

public class ByteCodeInstructionDemo implements Comparable {

    public static void main(String[] args) {
        ByteCodeInstructionDemo bs = new ByteCodeInstructionDemo();
        System.out.println(bs.sayHello());
        sayHi();
        Comparable comparable = bs;
        System.out.println(comparable.compareTo(null));
    }

    public String sayHello() {
        return "say Hello";
    }

    public static void sayHi() {
        System.out.println("say Hi");
    }

    @Override
    public int compareTo(Object o) {
        return -1;
    }
}

当前的JDK版本:

java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)

生成的部分Java字节码如下:

 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: new           #2                  // class com/xzy/demo/ByteCodeInstructionDemo
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: astore_1
         8: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: aload_1
        12: invokevirtual #5                  // Method sayHello:()Ljava/lang/String;
        15: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        18: invokestatic  #7                  // Method sayHi:()V
        21: aload_1
        22: astore_2
        23: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        26: aload_2
        27: aconst_null
        28: invokeinterface #8,  2            // InterfaceMethod java/lang/Comparable.compareTo:(Ljava/lang/Object;)I
        33: invokevirtual #9                  // Method java/io/PrintStream.println:(I)V
        36: return

加入invokedynamic指令的目的是为了在Java平台支持动态语言类型(JSR-292)。

lambda和invokedynamic

lambda最后实际的运行其实还是invokevirtual或者invokeinterfaceinvokedynamic只是推迟到到了运行时。运行时动态生成实现类(由调用java.lang.invoke.LambdaMetafactory.metafactory实现)。invokedynamic指令调用metafactory方法,会返回一个CallSite,此CallSite返回目标类型的一个匿名实现类, 此类关联编译时产生的方法。

关于类加载顺序

https://segmentfault.com/n/1330000019799723?from=timeline&isappinstalled=0

https://www.bilibili.com/video/av59801808

参考资料