淺談Java10 新特性
3 月 21 日,作為世界上使用最廣泛程式語言之一的 Java 迎來了第十個大版本。在 2017 年,甲骨文曾宣佈,Java 語言將每隔 6 個月提供一次更新,並承諾不會跳票,Java 10 正是在此規則下的首個大版本。需要注意的是 Java 9 和 Java 10 都不是 LTS 版本。和過去的 Java 大版本升級不同,這兩個只有半年左右的開發和維護期。而未來的 Java 11,也就是 18.9 LTS,才是 Java 8 之後第一個 LTS 版本(得到 Oracle 等商業公司的長期支援服務)。
Java 10 新特性
這次釋出的 Java 10,新帶來的特性並不多。
根據官網公開資料,共有 12 個 JEP(JDK Enhancement Proposal 特性加強提議),帶來以下加強功能:
JEP286,var 區域性變數型別推斷。
JEP296,將原來用 Mercurial 管理的眾多 JDK 倉庫程式碼,合併到一個倉庫中,簡化開發和管理過程。
JEP304,統一的垃圾回收介面。
JEP307,G1 垃圾回收器的並行完整垃圾回收,實現並行性來改善最壞情況下的延遲。
JEP310,應用程式類資料 (AppCDS) 共享,通過跨程序共享通用類元資料來減少記憶體佔用空間,和減少啟動時間。
JEP312,ThreadLocal 握手互動。在不進入到全域性 JVM 安全點 (Safepoint) 的情況下,對執行緒執行回撥。優化可以只停止單個執行緒,而不是停全部執行緒或一個都不停。
JEP313,移除 JDK 中附帶的 javah 工具。可以使用 javac -h 代替。
JEP314,使用附加的 Unicode 語言標記擴充套件。
JEP317,能將堆記憶體佔用分配給使用者指定的備用記憶體裝置。
JEP317,使用 Graal 基於 Java 的編譯器,可以預先把 Java 程式碼編譯成原生代碼來提升效能。
JEP318,在 OpenJDK 中提供一組預設的根證書頒發機構證書。開源目前 Oracle 提供的的 Java SE 的根證書,這樣 OpenJDK 對開發人員使用起來更方便。
JEP322,基於時間定義的釋出版本,即上述提到的釋出週期。版本號為\$FEATURE.\$INTERIM.\$UPDATE.\$PATCH,分別是大版本,中間版本,升級包和補丁版本。
部分新特性解析
1,迄今為止,在官方放出了Java 10少數新特性裡面,區域性變數型別推斷(local-variable type inference)
絕對是備受萬眾矚目的。它將我們常常在JS裡面使用的var
變數引入到語言特性中,把我們從那些冗長的變數宣告中解放出來。
這個語言功能在其他一些語言 (C#、JavaScript) 和基於 JRE 的一些語言 (Scala 和 Kotlin) 中,早已被加入。
在 Java 語言很早就在考慮,早在 2016 年正式提交了 JEP286 提議。後來舉行了一次公開的開發者調查,獲得最多建議的是採用類似 Scala 的方案,“同時使用 val 和 var”,約佔一半;第二多的是“只使用 var”,約佔四分之一。後來 Oracle 公司經過慎重考慮,採用了只使用 var 關鍵字的方案。
有了這個功能,開發者在寫這樣的程式碼時:
List<String> myList = new ArrayList<String>()
可以省去前面的型別宣告,而只需要
var list = new ArrayList<String>()
編譯器會自動推斷出 list 變數的型別。對於鏈式表示式來說,也會很方便:
var stream = blocks.stream();
使用範圍:區域性變數。更加確切的說法是:具有初始化器的區域性型別變數宣告。
2,應用程式類資料共享(AppCDS)
CDS 特性在原來的 bootstrap 類基礎之上,擴充套件加入了應用類的 CDS(Application Class-Data Sharing) 支援。
其原理為:在啟動時記錄載入類的過程,寫入到文字檔案中,再次啟動時直接讀取此啟動文字並載入。設想如果應用環境沒有大的變化,啟動速度就會得到提升。
我們可以想像為類似於作業系統的休眠過程,合上電腦時把當前應用環境寫入磁碟,再次使用時就可以快速恢復環境。
我在自己 PC 電腦上做以下應用啟動實驗。
首先部署 wildfly 12 應用伺服器,採用 JDK10 預覽版作為 Java 環境。另外需要用到一個工具 cl4cds[1],作用是把載入類的日誌記錄,轉換為 AppCDS 可以識別的格式。
A、安裝好 wildfly 並部署一個應用,具有 Angularjs, rest, jpa 完整應用技術棧,預熱後啟動三次,並記錄完成部署時間
分別為 6716ms, 6702ms, 6613ms,平均時間為 6677ms。
B、加入環境變數並啟動,匯出啟動類日誌
export PREPEND_JAVA_OPTS="-Xlog:class+load=debug:file=/tmp/wildfly.classtrace"
C、使用 cl4cds 工具,生成 AppCDS 可以識別的 cls 格式
/jdk-10/bin/java -cp src/classes/ io.simonis.cl4cds /tmp/wildfly.classtrace /tmp/wildfly.cls
開啟檔案可以看到內容為:
java/lang/Object id: 0x0000000100000eb0
java/io/Serializable id: 0x0000000100001090
java/lang/Comparable id: 0x0000000100001268
java/lang/CharSequence id: 0x0000000100001440
......
org/hibernate/type/AssociationType id: 0x0000000100c61208 super: 0x0000000100000eb0 interfaces: 0x0000000100a00d10 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar
org/hibernate/type/AbstractType id: 0x0000000100c613e0 super: 0x0000000100000eb0 interfaces: 0x0000000100a00d10 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar
org/hibernate/type/AnyType id: 0x0000000100c61820 super: 0x0000000100c613e0 interfaces: 0x0000000100c61030 0x0000000100c61208 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar
....
這個檔案用於標記類的載入資訊。
D、使用環境變數啟動 wildfly,模擬啟動過程並匯出 jsa 檔案,就是記錄了啟動時類的資訊。
export PREPEND_JAVA_OPTS="-Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=/tmp/wildfly.cls -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=/tmp/wildfly.jsa"
檢視產生的檔案資訊,jsa 檔案有較大的體積。
/opt/work/cl4cds$ ls -l /tmp/wildfly.*
-rw-rw-r-- 1 shihang shihang 8413843 Mar 20 11:07 /tmp/wildfly.classtrace
-rw-rw-r-- 1 shihang shihang 4132654 Mar 20 11:11 /tmp/wildfly.cls
-r--r--r-- 1 shihang shihang 177659904 Mar 20 11:13 /tmp/wildfly.jsa
E、使用 jsa 檔案啟動應用伺服器
export PREPEND_JAVA_OPTS="-Xshare:on -XX:+UseAppCDS -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=/tmp/wildfly.jsa"
啟動完畢後記錄時長,三次分別是 5535ms, 5333ms, 5225ms,平均為 5364ms,相比之前的 6677ms 可以算出啟動時間提升了 20% 左右。
這個效率提升,對於雲端應用部署很有價值。