通过 java.lang.module
我在类文档中阅读了以下内容:
@implNote ... is used at VM startup and so deliberately avoids using lambda and stream usages in code paths used during startup.
使用此处避免的 lambda 和流的原因是什么?它们可能产生的影响是什么?
插图将有助于更好地理解,但不是在这里寻求意见。
最佳答案
不依赖 lambda 和流(广泛使用 lambda)有助于避免在 VM Bootstrap 中进行冗余工作。这反过来又减少了启动时间和内存占用。
invokedynamic
JDK 中的机制相当复杂。它涉及许多java.lang.invoke.*
类,与方法句柄、Lambda 元工厂等相关,需要加载和初始化。此外,为了链接 invokedynamic
字节码,JVM 使用 ObjectWeb ASM 框架动态创建适配器。在运行时生成这样的类也需要时间和空间。
让我们测量一下在非常基本的场景中使用 lambda 而不是内部类的开销。我创建了两个类似的类,它们除了实例化内部类或 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();
}
}
然后我在打开类加载日志的情况下运行:
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
完整的输出是here .正如我们所见,Inner
需要 136 毫秒和 537 个加载类,而 Lambda
需要 161 毫秒和 620 个加载类。
因此,在这个简单的示例中,避免使用单个 lambda 有助于节省 25 毫秒的启动时间,同时减少加载 83 个类。
编辑
我描述的开销由两部分组成:
- 加载和初始化
java.lang.invoke.*
类 - 这是常量部分,只需完成一次。 - 链接特定的 lambda 调用站点 - 这需要调用 LambdaMetafactory Bootstrap 方法并生成用于调用目标方法的运行时适配器。这需要为每个 lambda 完成,因此这部分开销与代码中的 lambda 数量成正比。
关于java - 避免 VM 启动时使用的类使用 lambda 和流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45902215/