JAVA 9的新特性
java模塊系統 (Java Platform Module System)。
模塊系統的使用:
HTTP 2 客戶端:HTTP/2標準是HTTP協議的最新版本,新的 HTTPClient API 支持 WebSocket 和 HTTP2 流以及服務器推送特性。
新的版本號格式:$MAJOR.$MINOR.$SECURITY.$PATCH
private instance methods:方法上可以使用@SafeVarargs注解。
- diamond語法與匿名內部類結合使用。
- 下劃線_不能單獨作為變量名使用。
- 支持私有接口方法(您可以使用diamond語法與匿名內部類結合使用)。
Javadoc
- 簡化Doclet API。
- 支持生成html5格式。
- 加入了搜索框,使用這個搜索框可以查詢程序元素、標記的單詞和文檔中的短語。
- 支持新的模塊系統。
JVM
- 增強了Garbage-First(G1)并用它替代Parallel GC成為默認的垃圾收集器。
- 統一了JVM 日志,為所有組件引入了同一個日志系統。
- 刪除了JDK 8中棄用的GC組合。(DefNew + CMS,ParNew + SerialOld,Incremental CMS)。
REPL (JShell):交互式編程環境。
響應式流(Reactive Streams) API: Java 9中引入了新的響應式流 API 來支持 Java 9 中的響應式編程。
進程 API: 改進的 API 來控制和管理操作系統進程。引進 java.lang.ProcessHandle 及其嵌套接口 Info 來讓開發者逃離時常因為要獲取一個本地進程的 PID 而不得不使用本地代碼的窘境。
改進的 Stream API:改進的 Stream API 添加了一些便利的方法,使流處理更容易,并使用收集器編寫復雜的查詢。
Java 10的新特性
說了這么多,看Java 10都會有哪些特性來改變我們寫代碼的方式呢?!
1. 局部變量類型推斷
局部變量類型推斷可以說是Java 10中最值得注意的特性,Java語言開發人員為了簡化Java應用程序的編寫而采取的又一步簡化,如下圖所示。
//之前的代碼格式 URL url = new URL("http://www.oracle.com/"); URLConnection conn = url.openConnection(); Reader reader = new BufferedReader( new InputStreamReader(conn.getInputStream())) //java10中用var來聲明變量 var url = new URL("http://www.oracle.com/"); var conn = url.openConnection(); var reader = new BufferedReader( new InputStreamReader(conn.getInputStream()));
這個新功能將為Java增加一些語法糖 - 簡化它并改善開發者體驗。新的語法將減少與編寫Java相關的冗長度,同時保持對靜態類型安全性的承諾。
局部變量類型推斷將引入"var"關鍵字,也就是你可以隨意定義變量而不必指定變量的類型,如:
將被下面這個新語法所取代:
看完是不是有點JS的即視感???越來越像JS了嗎?!雖然類型推斷在Java中不是一個新概念,但在局部變量中確是很大的一個改進。
說到類型推斷,從JDK 5引進泛型,到JDK 7的"<>"操作符允許不綁定類型而初始化List,再到JDK 8的Lambda表達式,再到現在JDK 10的局部變量類型推斷,Java類型推斷正大刀闊斧的向前發展。
局部變量類型推薦僅限于如下使用場景:
- 本地變量初始化。
- 增強for循環中。
- 傳統for循環中聲明的索引變量。
- Try-with-resources 變量。?
Java官方表示,它不能用于以下幾個地方:
- 方法參數
- 構造函數參數
- 方法返回類型
- 字段
- try-catch表達式(或任何其他類型的變量聲明)
2. GC改進和內存管理
JDK 10中有2個JEP專門用于改進當前的垃圾收集元素。
第一個垃圾收集器接口是(JEP 304),它將引入一個純凈的垃圾收集器接口,以幫助改進不同垃圾收集器的源代碼隔離。
預定用于Java 10的第二個JEP是針對G1的并行完全GC(JEP 307),其重點在于通過完全GC并行來改善G1最壞情況的等待時間。G1是Java 9中的默認GC,并且此JEP的目標是使G1平行。
3. 線程本地握手(JEP 312)
JDK 10將引入一種在線程上執行回調的新方法,因此這將會很方便能停止單個線程而不是停止全部線程或者一個都不停。
4. 備用內存設備上的堆分配(JEP 316)
允許HotSpot VM在備用內存設備上分配Java對象堆內存,該內存設備將由用戶指定。
5. 其他Unicode語言 - 標記擴展(JEP 314)
目標是增強java.util.Locale及其相關的API,以便實現語言標記語法的其他Unicode擴展(BCP 47)。
6. 基于Java的實驗性JIT編譯器
Oracle希望將其Java JIT編譯器Graal用作linux / x64平臺上的實驗性JIT編譯器。
7. 根證書(JEP 319)
這個的目標是在Oracle的Java SE中開源根證書。
8. 根證書頒發認證
這將使OpenJDK對開發人員更具吸引力,它還旨在減少OpenJDK和Oracle JDK構建之間的差異。
9. 將JDK生態整合單個存儲庫(JEP 296)
此JEP的主要目標是執行一些內存管理,并將JDK生態的眾多存儲庫組合到一個存儲庫中。
10. 刪除工具javah(JEP 313)
從JDK中移除了javah工具,這個很簡單并且很重要。
Java 10嘗鮮
就像所有其他JDK版本一樣,Oracle已經發布了一個Java 10初始候選版本,Java開發者可以下載此版本用來測試新功能。如果你對Java 10抱有期待想提早嘗試,那么現在就開始吧!
11.Optional類
Optional類添加了新的方法orElseThrow(無參數版)。相比于已經存在的get方法,這個方法更推薦使用。
Java 11 新特性
- 支持Unicode 10.0,在jdk10中是8.0。
- 標準化HTTP Client
- 編譯器線程的延遲分配。添加了新的命令-XX:+UseDynamicNumberOfCompilerThreads動態控制編譯器線程的數量。
- 新的垃圾收集器—ZGC。一種可伸縮的低延遲垃圾收集器(實驗性)。
- Epsilon。一款新的實驗性無操作垃圾收集器。Epsilon GC 只負責內存分配,不實現任何內存回收機制。這對于性能測試非常有用,可用于與其他GC對比成本和收益。
- Lambda參數的局部變量語法。java10中引入的var字段得到了增強,現在可以用在lambda表達式的聲明中。如果lambda表達式的其中一個形式參數使用了var,那所有的參數都必須使用var。
Java 12 新特性
Shenandoah:一個低停頓垃圾收集器(實驗階段)
Java 12 中引入一個新的垃圾收集器:Shenandoah,它是作為一中低停頓時間的垃圾收集器而引入到 Java 12 中的,其工作原理是通過與 Java 應用程序中的執行線程同時運行,用以執行其垃圾收集、內存回收任務,通過這種運行方式,給虛擬機帶來短暫的停頓時間。
Shenandoah 垃圾回收器是 Red Hat 在 2014 年宣布進行的一項垃圾收集器研究項目,旨在針對 JVM 上的內存收回實現低停頓的需求。該設計將與應用程序線程并發,通過交換 CPU 并發周期和空間以改善停頓時間,使得垃圾回收器執行線程能夠在 Java 線程運行時進行堆壓縮,并且標記和整理能夠同時進行,因此避免了在大多數 JVM 垃圾收集器中所遇到的問題。
據 Red Hat 研發 Shenandoah 團隊對外宣稱,Shenandoah 垃圾回收器的暫停時間與堆大小無關,這意味著無論將堆設置為 200 MB 還是 200 GB,都將擁有一致的系統暫停時間,不過實際使用性能將取決于實際工作堆的大小和工作負載。
圖 1. Shenandoah GC 工作周期如下所示
Shenandoah GC
上圖對應工作周期如下:
- Init Mark 啟動并發標記 階段
- 并發標記遍歷堆階段
- 并發標記完成階段
- 并發整理回收無活動區域階段
- 并發 Evacuation 整理內存區域階段
- Init Update Refs 更新引用初始化 階段
- 并發更新引用階段
- Final Update Refs 完成引用更新階段
- 并發回收無引用區域階段
需要了解不是唯有 GC 停頓可能導致常規應用程序響應時間比較長。具有較長的 GC 停頓時間會導致系統響應慢的問題,但響應時間慢并非一定是 GC 停頓時間長導致的,隊列延遲、網絡延遲、其他依賴服務延遲和操作提供調度程序抖動等都可能導致響應變慢。使用 Shenandoah 時需要全面了解系統運行情況,綜合分析系統響應時間。各種 GC 工作負載對比如下所示:
圖 2. 各種 GC 工作負載對比
下面推薦幾個配置或調試 Shenandoah 的 JVM 參數:
- -XX:+AlwaysPreTouch:使用所有可用的內存分頁,減少系統運行停頓,為避免運行時性能損失。
- -Xmx == -Xmsv:設置初始堆大小與最大值一致,可以減輕伸縮堆大小帶來的壓力,與 AlwaysPreTouch 參數配合使用,在啟動時提交所有內存,避免在最終使用中出現系統停頓。
- -XX:+ UseTransparentHugePages:能夠大大提高大堆的性能,同時建議在 Linux 上使用時將 /sys/kernel/mm/transparent_hugepage/enabled 和 /sys/kernel/mm/transparent_hugepage/defragv 設置為:madvise,同時與 AlwaysPreTouch 一起使用時,init 和 shutdownv 速度會更快,因為它將使用更大的頁面進行預處理。
- -XX:+UseNUMA:雖然 Shenandoah 尚未明確支持 NUMA(Non-Uniform Memory Access),但最好啟用此功能以在多插槽主機上啟用 NUMA 交錯。與 AlwaysPreTouch 相結合,它提供了比默認配置更好的性能。
- -XX:+DisableExplicitGC:忽略代碼中的 System.gc() 調用。當用戶在代碼中調用 System.gc() 時會強制 Shenandoah 執行 STW Full GC ,應禁用它以防止執行此操作,另外還可以使用 -XX:+ExplicitGCInvokesConcurrent,在 調用 System.gc() 時執行 CMS GC 而不是 Full GC,建議在有 System.gc() 調用的情況下使用。
不過目前 Shenandoah 垃圾回收器還被標記為實驗項目,需要使用參數:- XX:+UnlockExperimentalVMOptions 啟用。更多有關如何配置、調試 Shenandoah 的信息,請參閱 henandoah wiki。
增加一套微基準套件
Java 12 中添加一套新的基本的微基準測試套件,該套微基準測試套件基于 JMH(Java Microbenchmark Harness),使開發人員可以輕松運行現有的微基準測試并創建新的基準測試,其目標在于提供一個穩定且優化過的基準,其中包括將近 100 個基準測試的初始集合,并且能夠輕松添加新基準、更新基準測試和提高查找已有基準測試的便利性。
微基準套件與 JDK 源代碼位于同一個目錄中,并且在構建后將生成單個 Jar 文件。但它是一個單獨的項目,在支持構建期間不會執行,以方便開發人員和其他對構建微基準套件不感興趣的人在構建時花費比較少的構建時間。
要構建微基準套件,用戶需要運行命令:make build-microbenchmark,類似的命令還有:make test TEST="micro:java.lang.invoke" 將使用默認設置運行 java.lang.invoke 相關的微基準測試。關于配置本地環境可以參照文檔 docs/testing.md|html。
Switch 表達式擴展(預覽功能)
Java 11 以及之前 Java 版本中的 Switch 語句是按照類似 C、C++ 這樣的語言來設計的,在默認情況下支持 fall-through 語法。雖然這種傳統的控制流通常用于編寫低級代碼,但 Switch 控制語句通常運用在高級別語言環境下的,因此其容易出錯性掩蓋其靈活性。
在 Java 12 中重新拓展了 Switch 讓它具備了新的能力,通過擴展現有的 Switch 語句,可將其作為增強版的 Switch 語句或稱為 "Switch 表達式"來寫出更加簡化的代碼。
Switch 表達式也是作為預覽語言功能的第一個語言改動被引入新版 Java 中來的,預覽語言功能的想法是在 2018 年初被引入 Java 中的,本質上講,這是一種引入新特性的測試版的方法。通過這種方式,能夠根據用戶反饋進行升級、更改,在極端情況下,如果沒有被很好的接納,則可以完全刪除該功能。預覽功能的關鍵在于它們沒有被包含在 Java SE 規范中。
在 Java 11 以及之前版本中傳統形式的 Switch 語句寫法如下:
清單 1. Switch 語句示例
int dayNumber; switch (day) { case MONDAY: case FRIDAY: case SUNDAY: dayNumber = 6; break; case TUESDAY: dayNumber = 7; break; case THURSDAY: case SATURDAY: dayNumber = 8; break; case WEDNESDAY: dayNumber = 9; break; default: throw new IllegalStateException("Huh? " + day); }
上面代碼中多處出現 break 語句,顯得代碼比較冗余,同時如果某處漏寫一段 break 語句,將導致程序一直向下穿透執行的邏輯錯誤,出現異常結果,同時這種寫法比較繁瑣,也容易出問題。
換做 Java 12 中的 Switch 表達式,上述語句寫法如下:
清單 2. Switch 表達式示例
int dayNumber = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; default -> throw new IllegalStateException("Huh? " + day); }
使用 Java 12 中 Switch 表達式的寫法,省去了 break 語句,避免了因少些 break 而出錯,同時將多個 case 合并到一行,顯得簡潔、清晰也更加優雅的表達邏輯分支,其具體寫法就是將之前的 case 語句表成了:case L ->,即如果條件匹配 case L,則執行 標簽右側的代碼 ,同時標簽右側的代碼段只能是表達式、代碼塊或 throw 語句。為了保持兼容性,case 條件語句中依然可以使用字符 : ,這時 fall-through 規則依然有效的,即不能省略原有的 break 語句,但是同一個 Switch 結構里不能混用 -> 和 : ,否則會有編譯錯誤。并且簡化后的 Switch 代碼塊中定義的局部變量,其作用域就限制在代碼塊中,而不是蔓延到整個 Switch 結構,也不用根據不同的判斷條件來給變量賦值。
Java 11 以及之前版本中,Switch 表達式支持下面類型: byte、char、short、int、Byte、Character、Short、Integer、enum、tring,在未來的某個 Java 版本有可能會允許支持 float、double 和 long (以及上面類型的封裝類型)。
引入 JVM 常量 API
Java 12 中引入 JVM 常量 API,用來更容易地對關鍵類文件 (key class-file) 和運行時構件(artefact)的名義描述 (nominal description) 進行建模,特別是對那些從常量池加載的常量,這是一項非常技術性的變化,能夠以更簡單、標準的方式處理可加載常量。
此項改進主要在新的 java.lang.invoke.constant 包中定義了一系列基于值的符號引用類型,能夠描述每種可加載常量。符號引用以純粹 nominal 的形式描述可加載常量,與類加載或可訪問 性上下文分開。同時有些類可以作為自己的符號引用(例如 String),而對于可鏈接常量,另外定義了一系列符號引用類型,具體包括: ClassDesc (Class 的可加載常量標稱描述符) ,MethodTypeDesc(方法類型常量標稱描述符) ,MethodHandleDesc (方法句柄常量標稱描述符) 和 DynamicConstantDesc (動態常量標稱描述符) ,它們包含描述這些常量的 nominal 信息。
改進 AArch64 實現
Java 12 中將只保留一套 AArch64 實現,刪除所有與 arm64 實現相關的代碼,只保留 32 位 ARM 端口和 64 位 aarch64 的端口。刪除此套實現將允許所有開發人員將目標集中在剩下的這個 64 位 ARM 實現上,消除維護兩套端口所需的重復工作。
當前 Java 11 中存在兩套 64 位 AArch64 端口,它們主要存在于 src/hotspot/cpu/arm 和 open/src/hotspot/cpu/aarch64 目錄中。這兩套代碼中都實現了 AArch64,Java 12 中將刪除目錄 open/src/hotspot/cpu/arm 中關于 64-bit 的這套實現,只保留其中有關 32-bit 的實現,余下目錄的 open/src/hotspot/cpu/aarch64 代碼部分就成了 AArch64 的默認實現。
使用默認類數據共享(CDS)存檔
類數據共享機制 (Class Data Sharing ,簡稱 CDS) ,允許將一組類預處理為共享歸檔文件,以便在運行時能夠進行內存映射以減少 Java 程序的啟動時間,當多個 Java 虛擬機(JVM)共享相同的歸檔文件時,還可以減少動態內存的占用量,同時減少多個虛擬機在同一個物理或虛擬的機器上運行時的資源占用。
自 Java 8 以來,在基本 CDS 功能上進行了許多增強、改進,啟用 CDS 后應用的啟動時間和內存占用量顯著減少。使用 Java 11 早期版本在 64 位 Linux 平臺上運行 HelloWorld 進行測試,測試結果顯示啟動時間縮短有 32 %,同時在其他 64 位平臺上,也有類似或更高的啟動性能提升。
Java 12 針對 64 位平臺下的 JDK 構建過程進行了增強改進,使其默認生成類數據共享(CDS)歸檔,以進一步達到改進應用程序的啟動時間的目的,同時也避免了需要手動運行:-Xshare:dump 的需要,修改后的 JDK 將在 lib/server 目錄中保留構建時生成的 CDS 存檔。
當然如果需要,也可以添加其他 GC 參數,來調整堆大小等,以獲得更優的內存分布情況,同時用戶也可以像之前一樣創建自定義的 CDS 存檔文件。
改善 G1 垃圾收集器,使其能夠中止混合集合
G1 是垃圾收集器,設計用于具有大量內存的多處理器機器,提高了垃圾回收效率。該垃圾收集器 設計的主要目標之一是滿足用戶設置的預期的 JVM 停頓時間,G1 采用一個高級分析引擎來選擇在收集期間要處理的工作量,此選擇過程的結果是一組稱為 GC 回收集的區域。一旦收集器確定了 GC 回收集 并且 GC 回收、整理工作已經開始,則 G1 收集器必須完成收集集合集的所有區域中的所有活動對象之后才能停止;但是如果收集器選擇過大的 GC 回收集,可能會導致 G1 回收器停頓時間超過預期時間。
Java 12 中將把 GC 回收集(混合收集集合)拆分為必需和可選兩部分,使 G1 垃圾回收器能中止垃圾回收過程。其中必需處理的部分包括 G1 垃圾收集器不能遞增處理的 GC 回收集的部分(如:年輕代),同時也可以包含老年代以提高處理效率。將 GC 回收集拆分為必需和可選部分時,需要為可選 GC 回收集部分維護一些其他數據,這會產生輕微的 CPU 開銷,但小于 1 %的變化,同時在 G1 回收器處理 GC 回收集期間,本機內存使用率也可能會增加,使用上述情況只適用于包含可選 GC 回收部分的 GC 混合回收集合。
在 G1 垃圾回收器完成收集需要必需回收的部分之后,便開始收集可選的部分,如果還有時間的話,但是粗粒度的處理,可選部分的處理粒度取決于剩余的時間,一次只能處理可選部分的一個子集區域。在完成可選收集部分的收集后,G1 垃圾回收器可以根據剩余時間決定是否停止收集。如果在處理完 必需處理的 部分后,屬于時間不足,總時間花銷接近預期時間,G1 垃圾回收器也可以中止可選部分的回收以達到滿足預期停頓時間的目標。
增強 G1 垃圾收集器,使其能自動返回未用堆內存給操作系統
上節中介紹了 Java 12 中增強了 G1 垃圾收集器關于混合收集集合的處理策略,這節主要介紹在 Java 12 中同時也對 G1 垃圾回收器進行了改進,使其能夠在空閑時自動將 Java 堆內存返還給操作系統,這也是 Java 12 中的另外一項重大改進。
目前 Java 11 版本中包含的 G1 垃圾收集器 暫時無法及時將已提交的 Java 堆內存返回給操作系統, G1 垃圾收集器僅在進行完整 GC (Full GC) 或并發處理周期時才能將 Java 堆返回內存。由于 G1 回收器盡可能避免完整 GC,并且只觸發基于 Java 堆占用和分配活動的并發周期,因此在許多情況下 G 1 垃圾回收器不能回收 Java 堆內存,除非有外部強制執行。
在使用云平臺的容器環境中,這種不利之處特別明顯。即使在虛擬機不活動,但如果仍然使用其分配的內存資源,哪怕是其中的一小部分,G1 回收器也仍將保留所有已分配的 Java 堆內存。而這將導致用戶需要始終為所有資源付費,哪怕是實際并未用到,而云提供商也無法充分利用其硬件。如果在次期間虛擬機能夠檢測到 Java 堆內存的實際使用情況,并在利用空閑時間自動將 Java 堆內存返還,則兩者都將受益。
為了盡可能的向操作系統返回空閑內存,G1 垃圾收集器將在應用程序不活動期間定期生成或持續循環檢查整體 Java 堆使用情況,以便 G 1 垃圾收集器能夠更及時的將 Java 堆中不使用內存部分返還給操作系統。對于長時間處于空閑狀態的應用程序,此項改進將使 JVM 的內存利用率更加高效。
如果應用程序為非活動狀態,在下面兩種情況下,G1 回收器會觸發定期垃圾收集:
- 自上次垃圾回收完成 以來已超過 G1PeriodicGCInterva l 毫秒, 并且此時沒有正在進行的垃圾回收任務。如果 G1PeriodicGCInterval 值為零表示禁用快速回收內存的定期垃圾收集。
- 應用所在主機系統上執行方法 getloadavg(),一分鐘內系統返回的平均負載值低于 G1PeriodicGCSystemLoadThreshold。如果 G1PeriodicGCSystemLoadThreshold 值為零,則此條件不生效。
如果不滿足上述條件中的任何一個,則取消當期的定期垃圾回收。等一個 G1PeriodicGCInterval 時間周期后,將重新考慮是否執行定期垃圾回收。
G1 定期垃圾收集的類型根據 G1PeriodicGCInvokesConcurrent 參數的值確定:如果設置值了,G1 垃圾回收器將繼續上一個或者啟動一個新并發周期;如果沒有設置值,則 G1 回收器將執行一個完整的 GC。在每次一次 GC 回收末尾,G1 回收器將調整當前的 Java 堆大小,此時便有可能會將未使用內存返還給操作系統。新的 Java 堆內存大小根據現有配置確定,具體包括下列配置:- XX:MinHeapFreeRatio、-XX:MaxHeapFreeRatio、-Xms、-Xmx。
默認情況下,G1 回收器在定期垃圾回收期間新啟動或繼續上一輪并發周期,將最大限度地減少應用程序的中斷。如果定期垃圾收集嚴重影響程序執行,則需要考慮整個系統 CPU 負載,或讓用戶禁用定期垃圾收集。
Java 13 新特性
下邊看看 JDK 13 中會帶來哪些新特性:
- JEP 350,Dynamic CDS Archives:擴展應用程序類-數據共享,以允許在 Java 應用程序執行結束時動態歸檔類。歸檔類將包括默認的基礎層 CDS(class data-sharing)存檔中不存在的所有已加載的應用程序類和庫類。
- JEP 351,ZGC: Uncommit Unused Memory:增強 ZGC 以將未使用的堆內存返回給操作系統。
- JEP 353,Reimplement the Legacy Socket API:使用易于維護和調試的更簡單、更現代的實現替換 java.net.Socket 和 java.net.ServerSocket API 使用的底層實現。
- JEP 354: Switch Expressions (Preview):可在生產環境中使用的 switch 表達式,JDK 13 中將帶來一個 beta 版本實現。switch 表達式擴展了 switch 語句,使其不僅可以作為語句(statement),還可以作為表達式(expression),并且兩種寫法都可以使用傳統的 switch 語法,或者使用簡化的“case L ->”模式匹配語法作用于不同范圍并控制執行流。這些更改將簡化日常編碼工作,并為 switch 中的模式匹配(JEP 305)做好準備。
- JEP 355,Text Blocks (Preview):將文本塊添加到 Java 語言。文本塊是一個多行字符串文字,它避免了對大多數轉義序列的需要,以可預測的方式自動格式化字符串,并在需要時讓開發人員控制格式。