Search code examples
javaperformancejvmbytecode

Invokedynamic factory creates lambdas as a singleton?


For example, I have two lambdas:

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

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

Invokedynamic operator will create it with special factory

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;

But I have some problems with bytecode reading. Does it mean, that in this case this factory will cache lambda creation (and exec2 will reuse instance)?

// 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$0()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$1()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$1()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$0()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
}

Solution

  • You need to understand what a call-site is, first, imo; to be able to understand where caching happens. Both exec1 and exec2 will create two separate instances of a Runnable interface; both will be cached on the call-site. May be this little snippet will help:

    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");
    } 
    

    Running this reveals:

    1878246837  Hi from lambda
    
    1878246837  Hi from lambda
    
    1995265320  Hi from lambda
    
    1995265320  Hi from lambda
    

    separate instances, but both cached on the call-site.

    Either way, looking at the byte-code will not tell you anything about that. what you could look at is the bootstrap method that invokedynamic will use : LambdaMetafactory::metafactory and understand what that will do.