還在用JDK8?JDK8、JDK11升級至JDK17的重要特性:一次價值千萬的全方位升級指南
一、前言
今天小編翻看了一下 JDK 官網,發現 JDK 都已經在出到了 JDK25 了。然后我相信還是有很多企業和個人依舊在用的是 JDK8。
圖片
SpringBoot 3.x 對于 JDK 的最低版本要求是 17。SpringBoot 4.0.0 的快照版和預覽版已經出了。
圖片
不可否認,JDK 8 是之前應用最廣泛,服務時間最長的一個版本。但是 Java 開發中泰斗級的開發框架 Spring Framework 6 和 Spring Boot 3 對 JDK 版本的最低要求是JDK 17。相信 Spring 對 JDK 17 是經過深度驗證的。為了使用 Spring 最新框架,很多團隊和開發者就必須升級到 Java 17 版本。
今天小編將繼續帶大家梳理 JDK 11 升級至 JDK 17 的一些重要特性。
為了讓大家更好的理解 JDK 11 升級到 JDK 17 帶來的重要特性,本文將從以下 4 個方面進行講解:
- 升級 JDK 17 的必要性
- 語言新特性
- 新工具和庫更新
- JVM 優化
二、升級 JDK 17 必要性
1. 版本支持
JDK 11 作為一個 LTS版本,已于2023年9月結束公共更新, 對應的補丁和安全警告等支持將持續至 2026 年。而JDK 17 作為當前的LTS版本將提供至少到 2026 年的支持時間框架。
2. Spring 框架要求
現代框架已全面轉向JDK 17,Spring Framework 6 和 Spring Boot 3 對 JDK 版本的最低要求是 JDK 17。
3. 性能提升
從JDK 11升級到JDK 17后:
- G1GC(默認)平均速度提升 8.66%;
- ParallelGC 提升 6.54%;
- Parallel GC 整體比 G1 GC 快 16.39%;
簡而言之,JDK17 更快,高吞吐量垃圾回收器比低延遲垃圾回收器更快。
這些性能改進直接轉化為業務應用的處理能力提升和基礎設施成本降低。
三、語言新特性
1. 記錄類(Records)
Records 最早出現在 Java 14、15 中,作為預覽特性引入的,直至在 Java 16 中成為正式版。
記錄類是一種新型的類聲明,專門用于充當不可變數據載體:
// 代替傳統的POJO類
public record User(String name, String email, int age) { }
// 自動生成構造函數、訪問器、equals、hashCode和toString
User user = new User("張三", "zhangsan@example.com", 25);
System.out.println(user.name()); // 輸出"張三"如上述代碼所示,Record類型自動提供了構造方法、訪問器(getter)、equals()、hashCode()、toString()方法以及一個解析器(parser),無需定義Class。使用記錄類可以減少約70%的樣板代碼,使代碼更加清晰易懂。
2. 密封類(Sealed Classes)
這個特性從 Java 15 的預覽版本晉升為正式版本。
密封類提供了對繼承層次結構的精確控制:
// 定義密封接口
public sealed interface Shape
permits Circle, Rectangle, Triangle {
double area();
}
// 允許的子類必須明確修飾符
public final class Circle implements Shape {
private final double radius;
public Circle(double radius) { this.radius = radius; }
@Override
public double area() { return Math.PI * radius * radius; }
}
public non-sealed class Rectangle implements Shape {
private final double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() { return width * height; }
}通過permits關鍵字明確聲明允許繼承的子類,增強了領域模型安全性,避免不受控的擴展。子類必須聲明為final、sealed或non-sealed,確保繼承關系的完整性。
3. instanceof 模式匹配增強
模式匹配(Pattern Matching)最早在 Java 14 中作為預覽特性引入,直至 Java 16 中成為正式版。
對 instanceof 的改進,主要目的是為了讓創建對象更簡單、簡潔和高效,并且可讀性更強、提高安全性。
// 舊的寫法
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
// 新寫法
if (obj instanceof String s) {
// s自動轉換為String類型
System.out.println(s.length());
}4. Switch表達式增強
switch 表達式增強最早在 Java 12 中作為預覽特性引入,直至 Java 14 中成為正式版本。
// 新的switch表達式
String formatted = switch (obj) {
case Integer i -> String.format("int: %d", i);
case String s && !s.isEmpty() -> String.format("str: %s", s);
case null -> "null value";
default -> "unknown";
};
// 支持yield返回值
String description = switch (day) {
case MONDAY, FRIDAY -> "工作日";
case SATURDAY, SUNDAY -> {
System.out.println("這是周末");
yield "周末";
}
default -> "未知";
};Switch表達式帶來了簡化式的編碼方式,提供了新的分支切換方式,即 -> 符號,右則表達式方法體在執行完分支方法之后,自動結束switch分支。
5. 文本塊(Text Blocks)
文本塊最早在 Java 13 中作為預覽特性引入,直至 Java 15 中成為正式版本。
文本塊極大改善了多行字符串的處理:
// JSON示例
String json = """
{
"name": "張三",
"age": 30,
"address": "北京市朝陽區"
}
""";
// SQL示例
String query = """
SELECT id, name, email
FROM users
WHERE status = 'ACTIVE'
ORDER BY created_date DESC
""";文本塊通過三引號語法定義,自動處理換行和縮進,使代碼中的長字符串更易讀和維護。
四、新工具和庫更新
1. Socket API 重構
在 Java 13 中對 Socket API 做了重做。
原有的 java.net.Socket 和 java.net.ServerSocketAPI 及其底層實現,自 JDK 1.0 就已存在。這套實現混合了遺留的 Java 和 C 代碼,維護和調試都非常痛苦。原有的 PlainSocketImpl 實現使用線程棧作為 I/O 緩沖區,這導致需要多次增加默認線程棧大小。它使用原生數據結構來支持異步關閉,這也成為了多年來可靠性和移植性問題的根源,并且還存在一些并發問題。
在 JDK 13 之前,通過使用 PlainSocketImpl 作為 SocketImpl 的具體實現。JDK 13 使用新的 NioSocketImpl 來替代原有的實現。
NioSocketImpl 的實現共享了 JDK 內部的 NIO(New I/O)架構,它不需要自己的原生代碼,并且使用了現有的緩存機制,不再需要使用線程棧。新代碼使用 java.util.concurrent 并發鎖代替同步方法,因此避免了很多并發問題。
新實現在 Socket 進行需要 timeout 的操作(如connect,accept,read)時,會采用非阻塞模式來替代。
新實現使用 java.lang.ref.Cleaner 機制在 SocketImpl 被垃圾回收后關閉端口。連接重置處理與舊有機制一樣,在連接重置后嘗試讀取端口會始終失敗。
2. 改進 NullPointerException
最早在 Java 14 中引入。
JDK14 以前的出現 NullPointerException 時,只能定位到所在異常行,無法定位具體是哪個變量。改進后的 NullPointerException,可以清晰描述具體變量,提升了空指針異常的可讀性。
開啟方式:
-XX:+ShowCodeDetailsInExceptionMessages3. 偽隨機數生成器
這個特性是為偽隨機數生成器 RPNG(Pseudo-Random Number Generators)增加了新的接口類型和實現,可以更容易地互換使用不同的算法,而且它還為基于流的編程方式提供了更好的支持。這個特性的目標有四個:
- 在應用程序中更容易地交替使用各種 PRNG 算法;
- 改進了對基于流的編程的支持,提供了 PRNG 對象流;
- 消除現有 PRNG 類中的重復代碼;
- 保留java.util.Random類的現有行為,做好向下兼容。
新增了java.util.random.RandomGenerator接口,作為所有 PRNG 算法的統一 API,提供了工廠類java.util.random.RandomGeneratorFactory,借助java.util.ServiceLoader.load()的能力加載各種 PRNG 算法實現,可以構造RandomGenerator實例。
我們遍歷一下看看有哪些 PRNG 算法:
RandomGeneratorFactory.all().forEach(factory -> {
System.out.println(factory.group() + ":" + factory.name());
});結果是:
LXM:L32X64MixRandom
LXM:L128X128MixRandom
LXM:L64X128MixRandom
Legacy:SecureRandom
LXM:L128X1024MixRandom
LXM:L64X128StarStarRandom
Xoshiro:Xoshiro256PlusPlus
LXM:L64X256MixRandom
Legacy:Random
Xoroshiro:Xoroshiro128PlusPlus
LXM:L128X256MixRandom
Legacy:SplittableRandom
LXM:L64X1024MixRandomLegacy:Random就是我們常用的java.util.Random,我們來試試看:
RandomGenerator randomGenerator = RandomGeneratorFactory.of("Random")
.create(System.currentTimeMillis());
System.out.println(randomGenerator.getClass());
System.out.println(randomGenerator.nextInt(10));結果是:
class java.util.Random
6 (這個值隨不同的運行結果不同)我們還可以使用流式編程方式批量獲取隨機數:
final IntStream ints = RandomGeneratorFactory.of("L128X128MixRandom")
.create()
.ints(10, 0, 100);
System.out.println(Arrays.toString(ints.toArray()));結果會得到 10 個隨機數字數組(每次運行結果不同):
[50, 16, 73, 4, 79, 32, 55, 34, 40, 53]五、JVM與性能改進
1. ZGC 垃圾回收器
ZGC 最早在 Java 11 中作為實驗性功能引入的,直至 Java 13 成為正式版本。
ZGC旨在實現亞毫秒級的最大暫停時間,即使處理TB級堆內存也能保持穩定。
相比于 Java 11,ZGC 在 Java 13 做了如下改進:
- 釋放未使用內存給操作系統
- 支持最大堆大小為 16TB
- 添加參數:-XX:SoftMaxHeapSize 來軟限制堆大小
啟用ZGC:
java -XX:+UseZGC -Xmx4g -jar your-application.jarZGC 釋放未使用內存的功能,默認情況下是開啟的,可以通過參數:-XX:-ZUncommit 顯式關閉。
如果將最小堆大小 (-Xms) 配置為等于最大堆大小 (-Xmx),則將隱式的禁用此功能。
通過配置參數:-XX:ZUncommitDelay = <seconds>(默認值 300 秒)實現延遲釋放。
2. G1GC 持續優化
雖然G1仍然是默認垃圾回收器,但JDK 17中的 G1GC 包含了可中止的混合收集集合、NUMA 可識別內存分配等改進,進一步降低了暫停時間。
Java 14 改進非一致性內存訪問(NUMA)系統上的 G1 垃圾收集器的整體性能,主要是對年輕代的內存分配進行優化,從而提高 CPU 計算過程中內存訪問速度。
啟動參數:
-XX:+UseNUMA通過這種方式來啟用可識別的內存分配方式,能夠提高一些大型計算機的 G1 內存分配回收性能。
3. 刪除 CMS 垃圾回收器
CMS 是實現老年代垃圾回收算法(標記-清除的方式進行內存回收)的垃圾回收期。在執行內存回收時,可以與用戶線程并發執行,,比較適合在追求 GC 速度的服務器上使用。
然后正是因為能夠與用戶線程并發執行這個特帶你,CMS 存在如下幾個缺點:
- 然后正式因為可以與用戶現場并發執行,在服務器 CPU 核數較小的情況下,容易造成比較高的系統負載。
- 在執行用戶線程時,會繼續創建新的對象、也會繼續釋放不用的對象。在 CMS 垃圾回收器執行標記不可達內存后,并行執行的用戶線程繼續釋放不可達的對象,這部分因為未被標記,所以無法在本輪回收周期內回收,只能等待下次回收。
- 在執行用戶現場時,會繼續創建新的對象,所以 CMS 垃圾回收期在執行期間,需要預留一些空間用來保存用戶新創建的對象,同時標記-清除算法勢必會產生內存碎片,當內存碎片過多時,將會給大對象分配帶來麻煩。
因此早在 Java 9 中,就已經決定棄使 CMS 回收器了,而這次在 Java 14 中,是徹底將其禁用,并刪除與 CMS 有關的選項,刪除與 CMS 有關的文檔。曾經輝煌一度的 CMS 回收器,現已成為了歷史。
4. 棄用 ParallelScavenge 和 SerialOld GC 的組合使用
由于組合使用 ParallelScavenge 和 SerialOld 這兩種垃圾回收期,卻要花費巨大工作量來進行維護,所以在 Java 14 版本中,考慮將這兩 GC 的組合棄用。
5. 禁用偏向鎖定
在 JDK 15 中,準備禁用和廢除偏向鎖,默認情況下禁用偏向鎖,同時棄用所有相關的命令行選項。
6. Shenandoah(低暫停時間)垃圾收集器
Shenandoah 垃圾收集器最早在 Java 12 中作為實驗性功能引入,此次終于變為產品特性。
Shenandoah 性能幾乎和 ZGC 差不多,但是 ZGC 是 Oracle JDK 的,而 Shenandoah 只存在于 OpenJDK 中,使用 -XX:+UseShenandoahGC 命令行參數啟用。
7. 彈性元空間
該特性能夠實現將未使用的 metaspace 內存釋放給操作系統,對于有大量類加載和卸載的應用程序是非常有幫助的。

































