16

I have read this article today regarding lambdas:

http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood

The article suggests, that lambdas are not implemented as anon inner classes (due to performance). It gives an example that a lambda expression can be compiled as a (static) method of class.

I have tried a very simple snippet:

private void run() {
    System.out.println(this);
    giveHello(System.out::println);
}

private void giveHello(Consumer<String> consumer) {
    System.out.println(consumer);
    consumer.accept("hello");
}

and the output is :

sample.Main@14ae5a5
sample.Main$$Lambda$1/168423058@4a574795
hello

So it's not the same instance. It's not some central "Lambda Factory" instance either..

How are lambdas implemented then?

Yanko
  • 784
  • 8
  • 16

4 Answers4

11

The expression itself, assuming you pass an actual lambda expression and not a method reference, is compiled as a separate, synthetic method. In addition to any formal arguments to the expected functional interface (e.g., a single String in the case of Consumer<String>), it will include arguments for any captured values.

At the code location where a lambda expression or method reference appears, an invokedynamic instruction is emitted. The first time this instruction is hit, a call is made into a bootstrap method on LambdaMetafactory. This bootstrap method will fix up an actual implementation of the target functional interface which delegates to the target method, and this is what gets returned. The target method is either the synthetic method representing the lambda body or whichever named method was provided using the :: operator. While a class that implements the functional interface is being created, the process is deferred; it does not happen at compile time.

Finally, the runtime patches the invokedynamic site with the bootstrap result1, which is effectively a constructor call to the generated delegate with any captured values passed in, including (possibly) an invocation target2. This alleviates the performance hit by removing the bootstrapping process for subsequent calls.


1 See java.lang.invoke end of chapter "timing of linkage", courtesy of @Holger.

2 In the case of a lambda which no captures, the invokedynamic instruction will usually resolve to a shared delegate instance that can be reused during subsequent calls, though this is an implementation detail.

Mike Strobel
  • 25,075
  • 57
  • 69
  • its the same if I write `s -> System.out.println(s)` – Yanko Oct 08 '14 at 13:02
  • That is a proper lambda expression, so yes, the above applies. The process would be similar if you passed in `System.out::println`, except that no synthetic method would be needed; the delegate would simply call directly into `println`. – Mike Strobel Oct 08 '14 at 13:03
  • 2
    It is *guaranteed* that `invokedynamic` uses the result of the first bootstrapping. See [`java.lang.invoke` end of chapter “timing of linkage”](http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/package-summary.html#indyinsn): “*Every dynamic call site transitions at most once from unlinked to linked, just before its first invocation. There is no way to undo the effect of a completed bootstrap method call.*” – Holger Oct 08 '14 at 13:33
  • @Holger Nice find! Updated my answer accordingly. – Mike Strobel Oct 08 '14 at 13:36
  • 2
    As an addendum, if the lambda expression has no captured values, the `invokedynamic` instruction will usually be resolved to a method handle returning a constant delegate object which therefore will always be the same. But that *is* implementation dependent. – Holger Oct 08 '14 at 13:40
  • @Holger I wondered whether the standard JVM made that kind of optimization. I kind of figured it did, but I hadn't seen it confirmed. Thanks! – Mike Strobel Oct 08 '14 at 13:42
  • 1
    See http://stackoverflow.com/a/23991339/2711488 – Holger Oct 08 '14 at 13:46
  • @MikeStrobel Thank you, your answer seems most complete. Reading through it I have a feeling, that the current impl. of lambda is indeed **just** a class (not necessarily inner, not necessarily static) that calls a synthetic method, only that the class is being "generated" on-the-fly (not during compile time). Is this correct or do I misunderstand you? Also, when the class is once generated, it is probably instantiated: does this happen for each invocation of the lambda, or is the instance kept somewhere? – Yanko Oct 08 '14 at 19:07
  • 3
    Yes, a class is being generated at runtime; you can see from your debug output that the synthetic class has the name `Main$$Lambda$1`. And whether a new instance is created on each invocation is an implementation detail, and depends on whether the lambda captures any non-constant values (see footnote #2). If there are captured values, a new instance will be created each time the call site is reached; if not, you may end up using a shared instance. – Mike Strobel Oct 08 '14 at 19:10
  • @MikeStrobel Thanks! I think that your answer would really benefit if you put the summary of these comments in it as well i.e. what happens (indy + LambdaMetafactory = class generation; class instantiation and perhaps caching). This is exactly what I was looking for. – Yanko Oct 09 '14 at 05:01
2

I asked myself the same question and found this video, a talk by Brian Goetz. It is very useful introduction to how lambdas are implemented in java.

Edit (summary): Watched it a while back, so this might not be completely correct. When the files are compiled, the compiler leaves a description of what the lambda should do. The JRE, then when running the code, will then decide how to implement the lambda. There are several options, inline, method reference, anonymous class. It will then use dynamic allocation to allocate the implementation.

PeterK
  • 1,697
  • 10
  • 20
  • 3
    Thanks! I will definitely watch it, but are you able to summarize it for Stack Overflow? – Yanko Oct 08 '14 at 12:53
  • You should not be downvoted hard for your 1st answer; the link is actually useful! Thanks – Yanko Oct 09 '14 at 05:02
-2

I'm not sure of it but i believe, that it is just compiling to Anonymous inner class. Consumer<T> is an interface, so i would say that your example is almost equals to

    giveHello(new Consumer<String>() {

        @Override
        public void accept(String t) {
            System.out.println(t);
        }
    });

EDIT

after some research, above answer is not complete and valid. Lambda expressions might be translated to anonymous inner class, but don't have to (and they usually don't).

T.G
  • 1,913
  • 1
  • 16
  • 29
  • It's not an anon class for sure :) – Yanko Oct 08 '14 at 12:54
  • i would deffend this opinion. It is on exact to anonymous inner class, but it is for sure its twin brother. http://programmers.stackexchange.com/a/195085. As mentioned here, in java world everything is a par of some class. – T.G Oct 08 '14 at 12:59
-2

Lambda's in Java 8 are so called functional interfaces, that is, anonymous interfaces with one default method.

heikkim
  • 2,955
  • 2
  • 24
  • 34
  • 3
    Lambdas in Java 8 are **assignable to** functional interface types, but that says nothing about how they are compiled or what gets instantiated at runtime. Also, a functional interface has one **non-default** method. – Mike Strobel Oct 08 '14 at 13:28