Going through the java.lang.module
I read amongst a class documentation the following:
@implNote ... is used at VM startup and so deliberately avoids using lambda and stream usages in code paths used during startup.
What are the causes from using lambda and streams that are avoided here and what are their possible impacts?
Illustrations would help understand better, not looking for opinions here though.
Not relying on lambdas and streams (that extensively use lambdas) helps to avoid doing redundant work at VM bootstrap. That in turn reduces startup time and memory footprint.
invokedynamic
machinery in JDK is rather complex. It involves many java.lang.invoke.*
classes related to Method Handles, Lambda Metafactories etc. that need to be loaded and initialized. Furthermore, to link invokedynamic
bytecode JVM dynamically creates an adapter using ObjectWeb ASM framework. Generating such classes in runtime also takes time and space.
Let's measure an overhead of using lambda instead of inner class in a very basic scenario. I create two similar classes that do nothing but instantiating either an inner class or a lambda:
class Inner {
public static void main(String[] args) {
Runnable r = new Runnable() { public void run() {} };
r.run();
}
}
class Lambda {
public static void main(String[] args) {
Runnable r = () -> {};
r.run();
}
}
Then I run both with class loading log turned on:
java -Xlog:class+load:file=inner.log Inner
java -Xlog:class+load:file=lambda.log Lambda
inner.log
[0.011s][info][class,load] opened: C:\Program Files\Java\jdk-9\lib\modules
[0.022s][info][class,load] java.lang.Object source: jrt:/java.base
[0.022s][info][class,load] java.io.Serializable source: jrt:/java.base
...
[0.136s][info][class,load] Inner$1 source: file:/C:/Andrei/
[0.136s][info][class,load] java.lang.Shutdown source: jrt:/java.base
[0.136s][info][class,load] java.lang.Shutdown$Lock source: jrt:/java.base
lambda.log
[0.011s][info][class,load] opened: C:\Program Files\Java\jdk-9\lib\modules
[0.022s][info][class,load] java.lang.Object source: jrt:/java.base
[0.022s][info][class,load] java.io.Serializable source: jrt:/java.base
...
[0.159s][info][class,load] Lambda$$Lambda$1/1282788025 source: Lambda
[0.159s][info][class,load] java.lang.invoke.InnerClassLambdaMetafactory$1 source: jrt:/java.base
[0.159s][info][class,load] java.lang.invoke.MethodHandleImpl$IntrinsicMethodHandle source: jrt:/java.base
[0.159s][info][class,load] java.lang.invoke.SimpleMethodHandle source: jrt:/java.base
[0.159s][info][class,load] sun.invoke.util.Wrapper$1 source: jrt:/java.base
[0.160s][info][class,load] java.lang.invoke.LambdaForm$MH/100555887 source: java.lang.invoke.LambdaForm
[0.160s][info][class,load] java.lang.invoke.LambdaForm$MH/1983747920 source: java.lang.invoke.LambdaForm
[0.160s][info][class,load] java.lang.Shutdown source: jrt:/java.base
[0.161s][info][class,load] java.lang.Shutdown$Lock source: jrt:/java.base
The full output is here. As we can see, Inner
required 136 ms and 537 loaded classes, while Lambda
took 161 ms and 620 loaded classes.
So, in this simple example avoiding a single lambda helped to save 25 ms of startup time with 83 less classes loaded.
EDIT
The overhead I've described consists of two parts:
java.lang.invoke.*
classes - this is the constant part which needs to be done only once.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With