Invokedynamic 工厂将 lambdas 创建为单例?

Invokedynamic factory creates lambdas as a singleton?

例如,我有两个 lambda:

Runnable exec1 = () -> {
     System.out.print("Hi from lambda");
};

Runnable exec2 = () -> {
     System.out.print("Hi from lambda");
};

Invokedynamic 运算符将使用特殊工厂创建它

java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

但是我在读取字节码时遇到了一些问题。这是否意味着,在这种情况下,该工厂将缓存 lambda 创建(并且 exec2 将重用实例)?

// access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 6 L0
    INVOKEDYNAMIC run()Ljava/lang/Runnable; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      ()V, 
      // handle kind 0x6 : INVOKESTATIC
      test/Main.lambda$main[=12=]()V, 
      ()V
    ]
    ASTORE 1
   L1
    LINENUMBER 10 L1
    INVOKEDYNAMIC run()Ljava/lang/Runnable; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      ()V, 
      // handle kind 0x6 : INVOKESTATIC
      test/Main.lambda$main()V, 
      ()V
    ]
    ASTORE 2
   L2
    LINENUMBER 13 L2
    RETURN
   L3
    LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
    LOCALVARIABLE exec Ljava/lang/Runnable; L1 L3 1
    LOCALVARIABLE exec2 Ljava/lang/Runnable; L2 L3 2
    MAXSTACK = 1
    MAXLOCALS = 3

  // access flags 0x100A
  private static synthetic lambda$main()V
   L0
    LINENUMBER 11 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "Hi from lambda"
    INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V
   L1
    LINENUMBER 12 L1
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 0

  // access flags 0x100A
  private static synthetic lambda$main[=12=]()V
   L0
    LINENUMBER 7 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "Hi from lambda"
    INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V
   L1
    LINENUMBER 8 L1
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 0
}

首先,您需要了解 call-site 是什么;能够理解 缓存 发生的位置。 exec1exec2 都会创建两个单独的 Runnable 接口实例;两者都将缓存在 call-site 上。也许这个小片段会有所帮助:

public static void main(String[] args) {
    useStatelessLambda1();
    useStatelessLambda1();

    useStatelessLambda2();
    useStatelessLambda2();
}

static void useStatelessLambda1() {
    Runnable exec1 = () -> {
        System.out.print("Hi from lambda");
    };

    System.out.print(exec1.hashCode() + "  ");
    exec1.run();
    System.out.println("\n");
}

static void useStatelessLambda2() {
    Runnable exec2 = () -> {
        System.out.print("Hi from lambda");
    };

    System.out.print(exec2.hashCode() + "  ");
    exec2.run();
    System.out.println("\n");
} 

运行 这表明:

1878246837  Hi from lambda

1878246837  Hi from lambda

1995265320  Hi from lambda

1995265320  Hi from lambda

单独的 个实例,但都缓存调用站点.

不管怎样,查看字节码不会告诉你任何相关信息。您可以查看 invokedynamic 将使用的 bootstrap 方法:LambdaMetafactory::metafactory 并了解它的作用。