Lambdas generated by `LambdaMetaFactory` are eagerly and unconditionally initialized with `Unsafe` right after class loading. This interferes with heap-snapshotting tools such as GraalVM Native Image: When a lambda is initialized during parsing, all of the super-interfaces with default methods will be initialized too. Static initializers of those interfaces pollute the heap snapshot and lead to unexpected behavior.
We would like to introduce a flag `jdk.internal.lambda.eagerlyInitialize`, which is `true` by default, and that can be used to prevent the eager initialization of lambdas. The proposed solution is summarised in the two following commits:
1) Introduces a new shape of non-capturing lambdas, where the lambda instance is kept in a static field:
https://github.com/graalvm/labs-openjdk-11/commit/00b9ecd85dedd0411837eee33635dd83e8b7def8
2) Introduces the conditional initialization of lambdas as a pure performance optimization:
https://github.com/graalvm/labs-openjdk-11/commit/273e8590a7b57c0c10d9213ca9e0ba581e2817b8
With the proposed scheme we get simpler code, that can be used for heap snapshotting and 1.5% better startup time for `HelloLambda.java`. The detailed numbers follow.
For all the tests we used the same configuration: `Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz` bound to a single socket with turbo disabled. We measured the Java 11 master (master), Java 11 with the first patch (patched) and Java 11 with the patch and lambdas pre-initialized with Unsafe (patched-init). For the instructions count, we used `perf stat` and executed 3 times a 100 runs and took the average. The standard deviation is very small for the interpreter case so I don’t report it. Here are the results:
(1) I measured only the number of instructions due to the OS noise related to time. The "Hello, Lambda!" numbers are with the JIT enabled.
| Branch | Instructions |
|-----------------|--------------------|
| master | 500,490,396 |
| patched | 492,933,691 |
| patched-init | 483,938,234 |
(2) The instruction count is measured in the interpreter (-Xint) and JIT and the time is measured only with the JIT enabled:
| Branch | Instructions (-Xint) | Instructions (JIT) | Time in us (JIT)|
|------------------|--------------------------|-------------------------|---------------------|
| master | 1,285,945,379 | 1,922,974,572 | 227,837 |
| patched | 1,315,914,375 | 2,058,175,796 | 233,457 |
| patched-init | 1,286,079,726 | 1,986,969,597 | 218,579 |