22

Consider the following code fragment:

public static Object o = new Object();

public static Callable x1() {
    Object x = o;
    return () -> x;
}

public static Callable x2() {
    return () -> o;
}

Method x2() will always return the same lamba object, while x1() will always create a new one:

    System.out.println(x1());
    System.out.println(x1());
    System.out.println(x2());
    System.out.println(x2());

It will printout something like this:

TestLambda$$Lambda$1/821270929@4a574795
TestLambda$$Lambda$1/821270929@f6f4d33
TestLambda$$Lambda$2/603742814@7adf9f5f
TestLambda$$Lambda$2/603742814@7adf9f5f

Where (in the JVM specification I suppose) is this rule of the lambda reuse described? How does JVM decide when to reuse or not?

Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • Not an answer to your question, but my guess is that every time you call `x1()` it creates a new `x` object, which obviously makes the lambda different. Contrary to that, `x2` is always the same as it returns a static object that doesn't change. – Crembo Apr 16 '15 at 12:48
  • @Crembo interesting guess, but if you will change it to `() -> new Object()` it will return same lambda object also. – Andremoniy Apr 16 '15 at 12:49
  • @Crembo BTW, in `x1()` no new objects are created – Andremoniy Apr 16 '15 at 12:50
  • Maybe the JVM cannot infer that and thus assumes it's a new object just to be safe? – Crembo Apr 16 '15 at 12:54
  • 4
    Everything has been explained [here](http://stackoverflow.com/a/23991339/2711488) and [here](http://stackoverflow.com/a/27524543/2711488) – Holger Apr 16 '15 at 13:27
  • @Holger I'm not agree with you. This is different things. – Andremoniy Apr 16 '15 at 13:30
  • 2
    It’s *exactly* the same thing. Just follow the link(s). The answers even cite the same parts of the specification (you have asked for) as the answers here. The close reason is not that the wording of the question matches, but that it *already has an answer* there. – Holger Apr 16 '15 at 13:37

5 Answers5

11

You can't be sure about the identity of the object returned for a lambda expression. It can be a new instance, or a pre-existing instance.

This is specified in JLS §15.27.4:

At run time, evaluation of a lambda expression is similar to evaluation of a class instance creation expression, insofar as normal completion produces a reference to an object. Evaluation of a lambda expression is distinct from execution of the lambda body.

Either a new instance of a class with the properties below is allocated and initialized, or an existing instance of a class with the properties below is referenced. If a new instance is to be created, but there is insufficient space to allocate the object, evaluation of the lambda expression completes abruptly by throwing an OutOfMemoryError.

Community
  • 1
  • 1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • Do you mean, that `x2()` in my example *can* return different objects? – Andremoniy Apr 16 '15 at 12:57
  • 1
    @Andremoniy Yes, it can. That is what the specs say. – Rohit Jain Apr 16 '15 at 12:58
  • With my full respect to you, I can not see in this part of `JLS` directive to the fact, that create new instance or use existing - is undefined behaivour. It just tells, that it could be reused, or could be created new. – Andremoniy Apr 16 '15 at 13:00
  • Imho, actually, on the jvm, it never will, updating my answer (long explanation). – uraimo Apr 16 '15 at 13:01
  • 1
    @Andremoniy Basically saying that - it could reuse instance or not, itself is the proof of non-unique identity of a lambda. So you should generally not depend upon the identity of lambdas - like, invoking `equals()` method on them (such thing will not have consistent results) – Rohit Jain Apr 16 '15 at 13:03
  • So, we should accept the fact that this JVM's decision is fully indefinite and nobody knows how it works. Yes? – Andremoniy Apr 16 '15 at 13:09
  • If you mean "generic JVM", i'd say that's correct, each jvm implementation is free to behave in either way. – uraimo Apr 16 '15 at 13:19
  • Ok, just wondering, do you agree with @Hoger that this question is duplicate of that question that he marked? – Andremoniy Apr 16 '15 at 13:31
  • 3
    @Andremoniy Well, the question might not be duplicate, but both have the same answers. – Rohit Jain Apr 16 '15 at 13:38
  • 1
    @Andremoniy: obviously, it’s the same, as this answer is exactly the same as [this answer](http://stackoverflow.com/a/27524543/2711488) – Holger Apr 16 '15 at 13:38
  • Not completely identical to the second one since you where asking precisely when (as in how the JVM decide if it has to) this caching happens. The answer to the first question instead explains in a clear way how this works, so in that case i'd say it's a possible duplicate. – uraimo Apr 16 '15 at 13:41
  • 1
    @uraimo: well, since this question consists of two questions in one, both being duplicates of older questions, everyone can always dispute a close vote for either. However I simply decided to refer to the one which contains a reference to the other (plus the specification) when closing. – Holger Apr 16 '15 at 13:50
6

After some investigations, it looks like it depends on the fact that the creation of lambda expressions is performed through invokedynamic and what you see is a side-effect of how invokedynamic behaves on the Oracle's JVM.

Decompiling your x1() and x2() methods:

public static java.util.concurrent.Callable x1();
Code:
  stack=1, locals=1, args_size=0
     0: getstatic     #2                  // Field o:Ljava/lang/Object;
     3: astore_0
     4: aload_0
     5: invokedynamic #3,  0              // InvokeDynamic #0:call:(Ljava/lang/Object;)Ljava/util/concurrent/Callable;
    10: areturn

public static java.util.concurrent.Callable x2();
Code:
  stack=1, locals=0, args_size=0
     0: invokedynamic #4,  0              // InvokeDynamic #1:call:()Ljava/util/concurrent/Callable;
     5: areturn

Relevant section of the Constant pool:

 #3 = InvokeDynamic      #0:#37         // #0:call:(Ljava/lang/Object;)Ljava/util/concurrent/Callable;
 #4 = InvokeDynamic      #1:#39         // #1:call:()Ljava/util/concurrent/Callable;

BootstrapMethods:

0: #34 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;
Method arguments:
  #35 ()Ljava/lang/Object;
  #36 invokestatic Test.lambda$x1$0:(Ljava/lang/Object;)Ljava/lang/Object;
  #35 ()Ljava/lang/Object;
1: #34 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;
Method arguments:
  #35 ()Ljava/lang/Object;
  #38 invokestatic Test.lambda$x2$1:()Ljava/lang/Object;
  #35 ()Ljava/lang/Object;

As explained here:

Because each invokedynamic instruction links (in general) to a different call site (we have two call sites, one for each xN function), the constant pool cache must contain a separate entry for each invokedynamic instruction. (Other invoke instructions can share CP cache entries, if they use the same symbolic reference in the constant pool.)

A Constant Pool cache entry ("CPCE"), when resolved, has one or two words of metadata and/or offset information.

For invokedynamic, a resolved CPCE contains a Method* pointer to a concrete adapter method providing the exact behavior of the call. There is also a reference parameter associated with the call site called the appendix, which is stored in the resolved_references array for the CPCE.

The method is called an adapter because (generally speaking) it shuffles arguments, extracts a target method handle from the call site, and invokes the method handle.

The extra reference parameter is called the appendix because it is appended to the argument list when the invokedynamic instruction is executed.

Typically the appendix is the CallSite reference produced by the bootstrap method, but the JVM does not care about this. As long as the adapter method in the CPCE knows what to do with the appendix stored with the CPCE, all is well.

As a corner case, if the appendix value is null, it is not pushed at all, and the adapter method must not expect the extra argument. The adapter method in this case could be a permanently linked reference to a static method with a signature consistent with the invokedynamic instruction. This would in effect turn the invokedynamic into a simple invokestatic. Many other such strength reduction optimizations are possible.

I'm interpreting that "This would in effect turn" as meaning that in such circumstances (adapter with no parameters) the invokedynamic will effectively behave like and invokestatic call and that the adapter will be cached and reused.

All this is specific to the Oracle's JVM, but i suspect that regarding to this aspect, this one is the most obvious choice and i'd expect to see something like this even in other jvm implementations.

Also, check this good answer for a cleaner rephrasing of that quote, way better than how i'd be able to explain it.

Community
  • 1
  • 1
uraimo
  • 19,081
  • 8
  • 48
  • 55
5

As it was already pointed out, the actual behavior is not specified by the JLS, a future version is allowed to derive from the current implementation as long as the JLS remains fullfilled.

Here is what happens in a current version of HotSpot:

Any lambda expression is bound via an invokedynamic call site. This call site requests a bootstrap method to bind a factory for an instance that implements the functional interface of the lambda expression. As arguments, any variables that are required to execute the lambda expression are handed to the factory. The body of the lambda expression is instead copied into a method inside of the class.

For your example, the desuggared version would look like the following code snipped with the invokedynamic instruction in angle brackets:

class Foo {
  public static Object o = new Object();

  public static Callable x1() {
    Object x = o;
    return Bootstrap.<makeCallable>(x);
  }

  private static Object lambda$x1(Object x) { return x; }

  public static Callable x2() {
    return Bootstrap.<makeCallable>();
  }

  private static void lambda$x2() { return Foo.o; }
}

The boostrap method (which is actually located in java.lang.invoke.LambdaMetafactory) is then asked to bind the call site on its first invocation. For lambda expressions, this binding will never change, the bootstrap method is therefore only called once. In order to being able to bind a class that implements the functional interface, the bootstrap method must first create a class at runtime which look like the following for example:

class Lambda$x1 implements Callable {
  private static Callable make(Object x) { return new Lambda$x1(x); }
  private final Object x; // constructor omitted
  @Override public Object call() { return x; }
}

class Lambda$x2 implements Callable {
  @Override public Object call() { return Foo.o; } 
}

After creating these classes, the invokedynamic instruction is bound to invoke the factory method that is defined by the first class to the call site. For the second class, no factory is created as the class is fully stateless. Therefore, the bootstrap method creates a singleton instance of the class and binds the instance directly to the call site (using a constant MethodHandle).

In order to invoke static methods from another class, an anonymous class loader is used for loading the lambda classes. If you want to know more, I recently summarized my findings on lambda expressions.

But again, always code against the spec, not the implementation. This can change!

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
3

(Edited this as my previous answer was rubbish!)

This document http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html provides an explanation.

These sections from that document should help answer your question...

Desugaring example -- "stateless" lambdas

The simplest form of lambda expression to translate is one that captures no state from its enclosing scope (a stateless lambda):

...and...

Desugaring example -- lambdas capturing immutable values

The other form of lambda expression involves capture of enclosing final (or effectively final) local variables, and/or fields from enclosing instances (which we can treat as capture of the final enclosing this reference).

Your second method (x2) is an example of the first type of lamba (a stateless one that captures no state from it's enclosing scope) and this is probably why the same lamba is returned in each case.

If you use javap to print out the generated bytecode, you can also see that there is a difference between the two blocks generated...

>javap -p -c L2.class

public class L2 {
  public static java.lang.Object o;

  public L2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static java.util.concurrent.Callable<java.lang.Object> x1();
    Code:
       0: getstatic     #2                  // Field o:Ljava/lang/Object;
       3: astore_0
       4: aload_0
       5: invokedynamic #3,  0              // InvokeDynamic #0:call:(Ljava/lang/Object;)Ljava/util/concurrent/Callable;
      10: areturn

  public static java.util.concurrent.Callable<java.lang.Object> x2();
    Code:
       0: invokedynamic #4,  0              // InvokeDynamic #1:call:()Ljava/util/concurrent/Callable;
       5: areturn

  private static java.lang.Object lambda$x2$1() throws java.lang.Exception;
    Code:
       0: getstatic     #2                  // Field o:Ljava/lang/Object;
       3: areturn

  private static java.lang.Object lambda$x1$0(java.lang.Object) throws java.lang.Exception;
    Code:
       0: aload_0
       1: areturn

  static {};
    Code:
       0: new           #5                  // class java/lang/Object
       3: dup
       4: invokespecial #1                  // Method java/lang/Object."<init>":()V
       7: putstatic     #2                  // Field o:Ljava/lang/Object;
      10: return
}
Community
  • 1
  • 1
BretC
  • 4,141
  • 13
  • 22
0

There is no way for the compiler to optimise x1() to return the same lambda -- the behaviour would then be different. Since o is not final, the lambda returned needs to capture the state of that field (with the x variable) as its value could change between calling x1() and invoking the returned lambda.

That's not to say that there aren't situations where the compiler theoretically could reuse the instance but doesn't (the other answers give some insight into that) -- only that this isn't one of those cases.

Joe Lee-Moyet
  • 1,804
  • 1
  • 20
  • 24
  • The lambda returned by `x1` doesn't use `o`. And `o` wouldn't have to be `final` anyway because it's not a variable. – a better oliver Apr 16 '15 at 21:11
  • @zeroflagL `x1()` depends on `o` - if an external caller gets a function object from `x1` and then sets `o` to refer to a different object, the returned function still needs to give the object that was in `o` when `x1` was called, therefore it wouldn't be correct for `x1` to always return the same Callable. – Joe Lee-Moyet Apr 16 '15 at 21:40
  • The returned function only references `x`. Even if `o` was final the lambda instances would still be different for every call. `o` doesn't matter. – a better oliver Apr 16 '15 at 21:48
  • I know the function only closes over `x`- my point is that in theory, were `o` to be final, a compiler could see that the behaviour of the returned function object is constant and could re-use the same Callable instance. Whether or not the Java specification allows such an optimisation is another question. – Joe Lee-Moyet Apr 16 '15 at 23:30
  • Ok, I understand your point, but think about it: If `o` was final, then you wouldn't assign it to a variable in the first place. So even in theory a compiler wouldn't optimize in that case because no one expects this to happen. – a better oliver Apr 17 '15 at 08:51