2017-08-27 48 views
7

的一类通过java.lang.module去避免Lambda和流使用我读之间的一类文档以下:在VM启动

@implNote ... is used at VM startup and so deliberately 
avoids using lambda and stream usages in code paths used during 
startup. 

什么是使用拉姆达的原因和流是在这里可以避免,它们可能产生什么影响?

插图将有助于更好地理解,而不是在这里寻找意见。

+3

你使用的是'jdk9'吗?我想,它还没有发布。对 ? – Ravi

+0

@Ravi是的,我([early access build](http://jdk.java.net/9/))。这个问题与它的发布没有关系,而是一个实现。 – nullpointer

+3

但是,如果有什么东西没有发布,那么怎么能有人告诉你他们的用法? – Ravi

回答

10

不依赖于lambdas和流(广泛使用lambda表达式)有助于避免在虚拟机引导时做冗余工作。这反过来又减少了启动时间和内存占用。

invokedynamic JDK中的机器相当复杂。它涉及许多与方法句柄,Lambda元函数等有关的类,这些类需要加载和初始化。此外,链接invokedynamic字节码JVM使用ObjectWeb ASM框架动态创建适配器。在运行时生成这样的类也需要时间和空间。

让我们测量在非常基本的场景中使用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引导方法并生成调用目标方法的运行时适配器。这需要为每个lambda完成,所以这部分开销与代码中的lambda数量成正比。
+0

另外,假设这里并提出,如果这可能会被添加到答案。 *单个lambda有助于节省25毫秒的启动时间,并减少了83个加载的类。*但这种增长会呈指数级增长,而不是乘以lambda的数量。我对么? – nullpointer

+3

@nullpointer我已经更新了答案。开销与lambda表达式的数量以及加载和初始化其他类所需的常量开销成正比。 – apangin