0

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
}
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
SlandShow
  • 443
  • 3
  • 13
  • 1
    can you clarify your question may be? I _think_ the answer (if I understood you correctly) is "yes", both `exec1` and `exec2` will create an instance that will be cached on the call-site. But there will be two instances, `exec1` and `exec2` (though they are the "same"), will still produce two separate instances. – Eugene Jul 25 '20 at 19:31
  • But anyway, why invokedynamic is more convinient way to reduce memory allocation? I thought, that in such case instance creation will be delegated to factory, which can re-use instances of lambdas – SlandShow Jul 25 '20 at 19:36
  • 1
    who said "more convenient way to reduce memory allocation"? It _could_ do that in theory, but how do you compare two lambdas? This is not trivial, at all. And even if that would be possible, you would not see it in the byte-code. Let me repeat: what is your _actual_ question here? – Eugene Jul 25 '20 at 19:39
  • @Eugene This topic is not true? https://www.baeldung.com/kotlin-inline-functions – SlandShow Jul 25 '20 at 19:40
  • My questiom was about invokedynamic factory and caching. So, exec1 and exec2 - different instances, right? – SlandShow Jul 25 '20 at 19:43

1 Answers1

2

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.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • Cool, thank you! So, call-site cache instance of lambda by method invocation, right? – SlandShow Jul 25 '20 at 20:02
  • 1
    I encourage you to create a non-stateless lambda (I named them `stateless` on purpose) and see what is going on. – Eugene Jul 25 '20 at 20:04
  • 2
    [this answer](https://stackoverflow.com/a/23991339/2711488) provides examples for investigating the caching behavior. But mind that the specification allows more (and also less) caching. A particular `LambdaMetafactory` may have additional caching logic, though the reference implementation (aka OpenJDK) has not. Another implementation might not even reuse instances of stateless lambdas… – Holger Jul 27 '20 at 14:50