Java 10正式釋出,帶來了這些新特性
點選上方“程式設計師小灰”,選擇“置頂公眾號”
有趣有內涵的文章第一時間送達!
本文轉載自公眾號 InfoQ
作者 | 張建鋒編輯 | 郭蕾北京時間 3 月 21 日,Oracle 官方宣佈 Java 10 正式釋出。這是 Java 大版本週期變化後的第一個正式釋出版本(詳見這裡),非常值得關注。你可以點選以下地址即刻下載:
http://www.oracle.com/technetwork/java/javase/downloads/index.html
去年 9 月,Oracle 將 Java 大版本週期從原來的 2-3 年,調整成每半年釋出一個大的版本。而版本號仍延續原來的序號,即 Java 8、Java 9、Java 10、Java 11.....
但和之前不一樣的是,同時還有一個版本號來表示釋出的時間和是否為 LTS(長期支援版本),比如 Java 10 對應 18.3。如下示例:
/jdk-10/bin$ ./java -version
openjdk version "10" 2018-03-20
OpenJDK Runtime Environment 18.3 (build 10+46)
OpenJDK 64-Bit Server VM 18.3 (build 10+46, mixed mode)
需要注意的是 Java 9 和 Java 10 都不是 LTS 版本。和過去的 Java 大版本升級不同,這兩個只有半年左右的開發和維護期。而未來的 Java 11,也就是 18.9 LTS,才是 Java 8 之後第一個 LTS 版本(得到 Oracle 等商業公司的長期支援服務)。
這種釋出模式已經得到了廣泛應用,一個成功的例子就是 Ubuntu Linux 作業系統,在偶數年 4 月的發行版本為 LTS,會有很長時間的支援。如 2014 年 4 月份釋出的 14.04 LTS,Canonical 公司和社群支援到 2019 年。類似的,Node.js,Linux kernel,Firefox 也採用類似的釋出方式。
Java 未來的釋出週期,將每半年釋出一個大版本,每個季度釋出一箇中間特性版本。這樣可以把一些關鍵特性儘早合併入 JDK 之中,快速得到開發者反饋,可以在一定程度上避免 Java 9 兩次被迫推遲釋出日期的尷尬。
下圖為 2017 年 JavaOne 大會時,Oracle 公開的未來 Java 版本釋出和支援週期圖。
這次釋出的 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. var 型別推斷。
這個語言功能在其他一些語言 (C#、JavaScript) 和基於 JRE 的一些語言 (Scala 和 Kotlin) 中,早已被加入。
在 Java 語言很早就在考慮,早在 2016 年正式提交了 JEP286 提議。後來舉行了一次公開的開發者調查,獲得最多建議的是採用類似 Scala 的方案,“同時使用 val 和 var”,約佔一半;第二多的是“只使用 var”,約佔四分之一。後來 Oracle 公司經過慎重考慮,採用了只使用 var 關鍵字的方案。
有了這個功能,開發者在寫這樣的程式碼時:
ArrayList<String> myList = new ArrayList<String>()
可以省去前面的型別宣告,而只需要
var list = new ArrayList<String>()
編譯器會自動推斷出 list 變數的型別。對於鏈式表示式來說,也會很方便:
var stream = blocks.stream();
...
int maxWeight = stream.filter(b -> b.getColor() == BLUE)
.mapToInt(Block::getWeight)
.max();
開發者無須宣告並且 import 引入 Stream 型別,只用 stream 作為中間變數,用 var 關鍵字使得開發效率提升。
不過 var 的使用有眾多限制,包括不能用於推斷方法引數型別,只能用於區域性變數,如方法塊中,而不能用於類變數的宣告,等等。
另外,我個人認為,對於開發者而言,變數型別明顯的宣告會提供更加全面的程式語言資訊,對於理解並維護程式碼有很大的幫助。一旦 var 被廣泛運用,開發者閱讀三方程式碼而沒有 IDE 的支援下,會對程式的流程執行理解造成一定的障礙。所以我建議儘量寫清楚變數型別,程式的易讀維護性有時更重要一些。
2. 統一的 GC 介面
在 JDK10 的程式碼中,路徑為 openjdk/src/hotspot/share/gc/,各個 GC 實現共享依賴 shared 程式碼,GC 包括目前預設的 G1,也有經典的 Serial、Parallel、CMS 等 GC 實現。
3. 應用程式類資料(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% 左右。
這個效率提升,對於雲端應用部署很有價值。
以上實驗方法參考於技術部落格 [2]。
4. JEP314,使用附加的 Unicode 語言標記擴充套件。
JDK10 對於 Unicode BCP 47 有了更多的支援,BCP 47 是 IETF 定義語言集的規範文件。使用擴充套件標記,可以更方便的獲得所需要的語言地域環境。
如 JDK10 加入的一個方法,
java.time.format.DateTimeFormatter::localizedBy
通過這個方法,可以採用某種數字樣式,區域定義或者時區來獲得時間資訊所需的語言地域本地環境資訊。
附:從連結 [3] 可以看到 JDK10 所有的方法級別改動。
5. 檢視當前 JDK 管理根證書。
自 JDK9 起在 keytool 中加入引數 -cacerts,可以檢視當前 JDK 管理的根證書。而 OpenJDK9 中 cacerts 為空,這樣就會給開發者帶來很多不變。
EP318 就是利用 Oracle 開源出 Oracle JavaSE 中的 cacerts 資訊,在 OpenJDK 中提供一組預設的根證書頒發機構證書,目前有 80 條記錄。
/jdk-10/bin$ ./keytool -list -cacerts
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 80 entries
verisignclass2g2ca [jdk], Dec 2, 2017, trustedCertEntry,
Certificate fingerprint (SHA-256): 3A:43:E2:20:FE:7F:3E:A9:65:3D:1E:21:74:2E:AC:2B:75:C2:0F:D8:98:03:05:BC:50:2C:AF:8C:2D:9B:41:A1
......
下一版本展望下一個 Java 大版本會是 Java 11,也是 Java 8 之後的 LTS 版本,預計會在今年的 9 月份釋出。目前只有四個 JEP,更多加強提議會逐步加入。
這個版本會充分發揮模組化的能力,把當前 JDK 中的關於 JavaEE 和 Corba 的部分移除,變得更加緊湊。
雖然 JDK9 最大的亮點是模組化,但 Java 業界廣泛接納並且適應需要一個過程。當前已經有一些支援模組化的類庫,如 log4j2,但大多數還未支援。
可以預見 JDK11 釋出之後,模組化特性就成為長期支援特性,會有越來越多的類庫提供對模組化的支援。
Java 依然會是最適合應用開發的語言和平臺,龐大的社群和廣泛的開發者,會不斷促使 Java 不斷完善優化,在各個程式設計領域繼續發揚光大。
對文中引用文章原作者表示致謝!引用的圖示,資料和方法都屬於原作者。下一個 Java 大版本會是 Java 11,也是 Java 8 之後的 LTS 版本,預計會在今年的 9 月份釋出。目前只有四個 JEP,更多加強提議會逐步加入。 這個版本會充分發揮模組化的能力,把當前 JDK 中的關於 JavaEE 和 Corba 的部分移除,變得更加緊湊。 雖然 JDK9 最大的亮點是模組化,但 Java 業界廣泛接納並且適應需要一個過程。當前已經有一些支援模組化的類庫,如 log4j2,但大多數還未支援。 可以預見 JDK11 釋出之後,模組化特性就成為長期支援特性,會有越來越多的類庫提供對模組化的支援。 Java 依然會是最適合應用開發的語言和平臺,龐大的社群和廣泛的開發者,會不斷促使 Java 不斷完善優化,在各個程式設計領域繼續發揚光大。 對文中引用文章原作者表示致謝!引用的圖示,資料和方法都屬於原作者。
[1]: https://simonis.github.io/cl4cds/
[2]: https://marschall.github.io/2018/02/18/wildfly-appcds.html
[3]:https://gunnarmorling.github.io/jdk-api-diff/jdk9-jdk10-api-diff.html#java.time.format.DateTimeFormatter
—————END—————
喜歡本文的朋友們,歡迎長按下圖關注訂閱號程式設計師小灰,收看更多精彩內容