java - 避免 VM 启动时使用的类使用 lambda 和流

标签 java lambda jvm startup java-9

通过 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 个类。

编辑

我描述的开销由两部分组成:

  1. 加载和初始化 java.lang.invoke.* 类 - 这是常量部分,只需完成一次。
  2. 链接特定的 lambda 调用站点 - 这需要调用 LambdaMetafactory Bootstrap 方法并生成用于调用目标方法的运行时适配器。这需要为每个 lambda 完成,因此这部分开销与代码中的 lambda 数量成正比。

关于java - 避免 VM 启动时使用的类使用 lambda 和流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45902215/

相关文章:

Java:从字节数组中删除连续的零段

java - 在android studio中的java 8中使用lambda

python - 为什么这个 lambda 操作不起作用?

java - 强制 JVM 尽早收集垃圾并减少尽早使用的系统内存

Java 内存感知缓存

java - 在servlet中运行java代码之前加载资源

java - 奇怪 - 第三个 @PathVariable 在 Spring 中不起作用

java - java.library.path 的目的

java - 如何将键转换为字符串,反之亦然

node.js - 为什么使用 pg-promise AWS Lambda 执行时间很长