Lambda in the memory
Is lambda just like an (anonymous) inner class?
NO — when talking about closures. This is a great article about Lambda limitations and closure, including a comparison to Javascript.
YES — when talking about memory allocation — in case that inner class is declared properly. This is what I’m going to discuss further in this article.
Stateless — having no reference to the enclosing scope
Let’s check Example 1:
Note: I avoid using Method Reference or any too-short syntax so we can see the lambda expression easier. 😳
Even lambdaPrinter
and myPrinter
are used similarly, they’re stored differently. myPrinter
is a normal local variable which refers to aPrinter
instance in Heap. When myPrinter = null
or main()
is finished, that instance is garbage-collected as usual.
However, lambdaPrinter
does not. Since this lambda has no reference to its enclosing scope, it is stored as a static variable which is NOT a target of Garbage Collector. Let’s call it a stateless lambda.
Here’s how they look in the Heap:

So, if myPrinter
is static, would it be treated like lambdaPrinter
? YES. Or actually, we only need to define Printer
as a static class to tell JVM that it has no reference to the enclosing instance. In this case, the Heap looks like:

And this is the proper way to define a stateless inner class.
Stateful lambda
In contrast to stateless lambdas, stateful lambdas call external local variables (variables which are defined outside of the lambda’s body block) or access to a field or method of its enclosing instance.
In this case, a lambda closure is created with references to the external local instances and the enclosing instance. These instances are passed into the lambda function — or I can call it as lambda instance now since it could be garbage-collected just like a normal instance of a class.
In Example 2, lambdaPrinter
accesses to myStr
local variable and this
instance via this.superPrint
.
And the Heap looks like:

You can see two references of LambdaLifecycle
and String
instances are COPIED into to lambdaPrinter
closure as two arguments. In case the local variables are primitive, their values are copied. Because of that, those local variables are not allowed to be changed or re-assigned so the lambda can find those values/references when executing later. For instance:
Consumer<String> lambdaPrinter = str -> {
this.superPrint(myStr); // compile error
myStr = "foo"; // because of this
enclosingField = "bar";
};
That’s why Variable used in lambda expression should be final or effectively final.
To sum up, a lambda function is stateful when it accesses references outside its body (variables, enclosing scope). Stateful lambda can be collected by GC. Otherwise, it’s stateless — just like a static inner class.
In Java Lambda and the Garbage Collector [part 2], I demonstrate how to test if GC correctly collects lambdas.