Does a Lambda Expression Create an Object on the Heap Every Time It's Executed

Does a lambda expression create an object on the heap every time it's executed?

It is equivalent but not identical. Simply said, if a lambda expression does not capture values, it will be a singleton that is re-used on every invocation.

The behavior is not exactly specified. The JVM is given big freedom on how to implement it. Currently, Oracle’s JVM creates (at least) one instance per lambda expression (i.e. doesn’t share instance between different identical expressions) but creates singletons for all expressions which don’t capture values.

You may read this answer for more details. There, I not only gave a more detailed description but also testing code to observe the current behavior.


This is covered by The Java® Language Specification, chapter “15.27.4. Run-time Evaluation of Lambda Expressions”

Summarized:

These rules are meant to offer flexibility to implementations of the Java programming language, in that:

  • A new object need not be allocated on every evaluation.

  • Objects produced by different lambda expressions need not belong to different classes (if the bodies are identical, for example).

  • Every object produced by evaluation need not belong to the same class (captured local variables might be inlined, for example).

  • If an "existing instance" is available, it need not have been created at a previous lambda evaluation (it might have been allocated during the enclosing class's initialization, for example).

Is lambda really an object or not?

There are a couple bits that need untangling here.

A lambda is an object. In particular, it's an instance of a functional interface, which has only one abstract method.

Not every occurrence of a lambda makes a new object.

invokedynamic can, but is not required to, make a new object.

invokedynamic is used to get lambda objects, whether or not they are newly created.

Java lambdas heap dump - Instance of lambda not getting garbage collected

As elaborated in Does a lambda expression create an object on the heap every time it's executed?, a non-capturing lambda expression will be remembered and reused, which implies that it is permanently associated with the code that created it. That’s not different to, e.g. string literals whose object representation stays in memory as long as the code containing the literal is alive.

This is an implementation detail. It doesn’t have to be that way, but the reference implementation and hence, all commonly used JREs do it that way.

A non-capturing lambda expression is a lambda expression that uses no (non-constant) variables of the surrounding context and does not use this, neither implicitly nor explicitly. So it bears no state, hence, consumes a tiny amount of memory. There is also no possibility to create a leak regarding other objects, as having references to other objects is what makes the difference between non-capturing and capturing lambda expressions and likely is the main reason why capturing lambda expressions are not remembered that way.

So the maximum number of such never-collected instances is equal to the total number of lambda expression in your application, which might be a few hundred or even thousands, but still small compared to the total number of objects the application will ever create. As explained in Function.identity() or t->t, putting a lambda expression into a factory method instead of repeating it in the source code, can reduce the number of instances. But given the rather small total number of objects, that’s rarely a concern. Compare with the number of the already mentioned string literals or the Class objects which already exist in the runtime…

Will methods in lambda only be evaluated when the lambda is executed?

Consider the following snappet:

Runnable runnable = () -> expensiveMethod();  // Runnable not called
firstMethodCall(); // Runnable not called
secondMethodCall(); // Runnable not called
runnable.run(); // HERE IT COMES, Runnable IS called

The lambda expression is nothing else than the implementation of an anonymous class with one method (this secures @FunctionalInterface annotation).

Runnable runnable = new Runnable() {
void run() {
expensiveMethod();
}
};

// runnable's method is not executed since the method run is not called
// the runnable.run() invokes the expensiveMethod()

The Java 8 specification 15.27. Lambda Expressions explains that the expression is called when the appropriate method of the functional interface is invoked:

Evaluation of a lambda expression produces an instance of a functional interface (§9.8). Lambda expression evaluation does not cause the execution of the expression's body; instead, this may occur at a later time when an appropriate method of the functional interface is invoked.

Does lamba expression automatically create new object for its entry parameters?

c is actually only a placeholder, like a parameter in a method would be (which does not differ from the functioning of the lambda here).

myClass.myMethod(c -> {my overridden code});

is the equivalent of the following

myClass.myMethod(new Consumer<String>(){
@Override
public void accept(String c) {
{my overridden code}
}
}

So the answer to your question is : No. The lambda represents a method, a function but is not an executable piece by itself, it has to be invoked with outside parameters.

Can someone help to explain how the lambda expression works in this context?

If we're talking about this Lambda expression:

        grid.addColumn(contact -> contact.getStatus().getName())

, it creates a column definition for the Grid. By itself, it doesn't do anything yet.

Once you add data into the Grid, this column definition will be used to determine what will be shown in that column.

Each row in the Grid represents an item in the data set. Your example doesn't show the Grid's generic type, but based on the variable naming, I'm going to assume it's something like Grid<Contact>. So your grid is a component that displays Contacts.

Let's look at the lambda expression contact -> contact.getStatus().getName(). The lambda expression's left side, contact is the input. The right side, contact.getStatus().getName() is the output. So putting all this together, for each row in the Grid, we invoke this expression. We're processing the current row's Contact data item, and we fetch .getStatus().getName() as the value to display for that row, in this current column. So the value to display in the column is the name of the status of the contact. In another column, we display contact.getCompany().getName() - the company name of the contact.

Both of the addColumn(...) lines end with .setHeader() calls - this is a builder which works on the value returned from the previous method call. In those setHeader() calls, the header of the column is set.

Java 8 Lambda expression with Serialization

You can create a serializable lambda expression via

Collections.sort(people, (Comparator<Person>&Serializable)
(p1, p2) -> p1.getLastName().compareTo(p2.getLastName()));

but it should be noted, that creating a Comparator via

(p1, p2) -> p1.getLastName().compareTo(p2.getLastName())

bears a discouraged redundancy. You are calling getLastName() twice and have to care to invoke it on the right parameter variable in either case. It is more straight-forward to use

Comparator.comparing(Person::getLastName)

instead. You can also make this comparator serializable, though that implies loosing much of the conciseness:

Collections.sort(people,
Comparator.comparing((Function<Person,String>&Serializable)Person::getLastName));

This is also more robust. The serialized form of a lambda expression contains a reference to the implementation method, which is in the first variant a synthetic method with a compiler generated name that might change when you use another lambda expression within the defining method. In contrast, Person::getLastName points the the named method getLastName as implementation method (at least with javac).

But generally, serializable lambda expressions might contain surprising compiler dependencies and should be used with care.

Since they are meant to describe behavior rather than data, there is no point in long-term storage of them anyway. For transferring them between JVMs with the same code base, they are sufficient.

Are java lambda functions unique (different hashCode and not equals)?

According to JLS 15.27.4 (emphasis mine):

The value of a lambda expression is a reference to an instance of a class with the following properties:

  • [...]
  • The class overrides no other methods of the targeted functional interface type or other interface types mentioned above, although it may override methods of the Object class.

Since it says "may", the class of the lambda might have a custom implementation of equals & hashCode, or it might not, depending on the implementation.

Also note:

These rules are meant to offer flexibility to implementations of the
Java programming language, in that:

  • A new object need not be allocated on every evaluation.

  • Objects produced by different lambda expressions need not belong to
    different classes (if the bodies are identical, for example).

  • Every object produced by evaluation need not belong to the same class
    (captured local variables might be inlined, for example).

  • If an "existing instance" is available, it need not have been created
    at a previous lambda evaluation (it might have been allocated during
    the enclosing class's initialization, for example).

An implementation could potentially see that both of your lambdas have identical bodies, and produce just one class for your lambda. That class's equals method could be overridden to return true for any object that is also an instance of that same class. It could even use the same object for both of your lambdas! In either case, if you add both lambdas to your set, your set would only have one element.



Related Topics



Leave a reply



Submit