2017-08-27 44 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