JDK11 升級 JDK17 最全實踐乾貨來了

来源:https://www.cnblogs.com/jingdongkeji/archive/2023/11/14/17831162.html
-Advertisement-
Play Games

從JDK11到JDK17,到底帶來了哪些特性呢?亞毫秒級的ZGC效果到底怎麼樣呢?值得我們升級嗎?而且升級過程會遇到哪些問題呢?帶著這些問題,本篇文章將帶來完整的JDK11升級JDK17最全實踐。 ...


1、前言

上篇文章給大家帶來了JDK8升級JDK11的最全實踐,相信大家閱讀後已經對JDK11有了比較深入的瞭解。2021年9月14日,Oracle發佈了可以長期支持的JDK17版本,那麼從JDK11到JDK17,到底帶來了哪些特性呢?亞毫秒級的ZGC效果到底怎麼樣呢?值得我們升級嗎?而且升級過程會遇到哪些問題呢?帶著這些問題,本篇文章將帶來完整的JDK11升級JDK17最全實踐。

2、為什麼升級JDK17

1)長期支持版本

JDK17是Oracle官方在2021年9月14日發佈的一個長期支持(LTS)版本,意味著它將獲得長期的更新和支持,有助於保持程式的穩定性和可靠性。

2)性能提升

更好的垃圾回收器。綜合評估,從Java 8 升級到 Java 11,**G1GC平均速度提升16.1%,ParallelGC為4.5%****,從Java 11 升級到 Java 17,G1GC平均速度提升8.66%,ParallelGC為6.54%**(基於OptaPlanner的用例基準測試表明)

最大的亮點是帶來了穩定版的ZGC垃圾回收器,達到亞毫秒級停頓。

3)新語法和特性

Switch表達式簡化、Text Blocks文本塊、instanceof 的模式匹配升級和NullPointerException提示信息改進等

4)支持最新的技術和框架

Spring framework6 和Spring Boot3 都預設使用 Java 17作為最低版本

3、升級後壓測效果

先給出結論:

1、JDK17相對於JDK8和JDK11,所有垃圾回收器的性能都有很明顯的提升,特別是穩定版的ZGC垃圾回收器

2、不論任何機器配置下,都推薦使用ZGC,ZGC的停頓時間達到亞毫秒級,吞吐量也比較高

我在JDOS平臺上選擇了不同配置的機器(2C4G、4C8G、8C16G),並分別使用JDK8、JDK11和JDK17進行部署和壓測。

整個壓測過程限時60分鐘,用180個虛擬用戶併發請求一個介面,每次介面請求都創建512Kb的數據。最終產出不同GC回收器的各項指標數據,來分析GC的性能提升效果。

以下是壓測的性能情況:

4、OracleJDK 和 OpenJDK 的選擇

2021年9月,Oracle宣佈JDK17可以免費商用,直到下一個 LTS 版本之後繼續提供整整一年,同時Oracle 將繼續按照自 Java 9 以來的相同版本和時間表提供GPL下的Oracle OpenJDK 版本。

2023年9月,OracleJDK發佈了新的LTS版本 JDK21,這就意味著從2024年9月開始,在生產環境使用 OracleJDK17 將需要付費。

參考: https://www.oracle.com/hk/java/technologies/downloads/#java17

OracleJDK和OpenJDK這兩個之間沒有真正的技術差別,因為針對Oracle JDK構建過程是基於OpenJDK的。自從JDK11開始,OracleJDK和OpenJDK在功能上基本相同,所以推薦使用 OpenJDK17 或其他開源的JDK版本,這些開源版本都是基於OpenJDK構建並提供長期支持的,比如:AdoptOpenJDKRedHatOpenJDK。

官方參考: https://blogs.oracle.com/java/post/oracle-jdk-releases-for-java-11-and-later

5、JDK11到JDK17帶來了哪些新特性

5.1、JVM改進

1、ZGC垃圾回收器從實驗性功能更改為正式產品功能,從JDK11引入以來,經過持續的迭代升級,目前已經足夠穩定。需要手動開啟,開啟方式:-XX:+UseZGC

2、G1垃圾回收器仍然作為預設垃圾回收器,進行改進升級,主要包括可中止的混合收集集合、NUMA 可識別記憶體分配等

3、JDK14開始刪除 CMS 垃圾回收器

4、JDK14開始棄用 ParallelScavenge 和 SerialOld GC 的組合使用

5、JDK15禁用偏向鎖,預設禁用:-XX:+UseBiasedLocking

6、NullPointerException 提示信息改進

JDK14以前的出現NullPointerException時,只能定位到所在異常行,無法定位具體是哪個變數。改進後的NullPointerException,可以清晰描述具體變數,提升了空指針異常的可讀性。

5.2、新語法特性

5.2.1、Switch表達式簡化

switch表達式帶來了簡化式的編碼方式,提供了新的分支切換方式,即 -> 符號,右則表達式方法體在執行完分支方法之後,自動結束 switch 分支,同時 -> 右則方法塊中可以是表達式、代碼塊或者是手動拋出的異常

參考: https://openjdk.org/jeps/361

傳統寫法

新寫法

5.2.2、Text Blocks文本塊

參考: https://openjdk.org/jeps/378

通過編寫 """,來減少轉義字元和換行符,達到簡化代碼和提高代碼可讀性的目的

5.2.3、Record類型

參考: https://openjdk.org/jeps/395

record 是 JDK 14 引入的關鍵字,用於聲明不可變的數據類。它適用於存儲純粹的值類型數據,如介面傳輸數據、坐標點和只讀的日誌記錄。與 lombok 相比,record 簡化了定義純粹數據類型的過程。由於 record 類是不可變的,成員變數只能設置一次且無法更改,無需提供顯式的 setter() 方法。

1、定義Point類,使用關鍵字record,未定義get/set

2、查看編譯後的位元組碼文件

3、使用Point類

5.2.4、instanceof 的模式匹配升級

  • instanceof類型判斷再也不需要強制轉換

參考: https://openjdk.org/jeps/394

5.2.5、密封的類和介面

參考: https://openjdk.org/jeps/409

JDK15開始,引入了sealed普通類或介面類,這些類只允許被指定的類或者interface進行擴展和實現。

使用修飾符sealed,您可以將一個類聲明為密封類。密封的類使用關鍵字permits列出可以直接擴展它的類。子類可以是最終的,非密封的或密封的

比較實用的一個特性,可以用來限制類的層次結構

5.2.6、其他優化和升級

感興趣的同學,推薦閱讀OpenJDK官方文檔說明,從JDK11到JDK17的改動: https://openjdk.org/projects/jdk/17/jeps-since-jdk-11

6、升級步驟

6.1、JDK選擇

OpenJDK17下載:https://jdk.java.net/archive/

行雲鏡像:jdt-base-tomcat/java-jdt-centos7.4-openjdk-17.0.2-tomcat8.0.53

6.2、pom編譯配置升級

maven編譯所需JDK升級至17

<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

6.3、SpringBoot升級

SpringBoot版本升級到2.7.15,Spring版本升級為5.3.29

為什麼不升級到SpringBoot3?

Spring Boot 3.0最低要求 Java 17,SpringBoot3.0帶來了很多變化,和SpringBoot2差異較大。 考慮到公司很多中間件都是基於SpringBoot2構建的,所以此處推薦升級到SpringBoot2的最高版本2.7.15。

POM升級

<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.7.15</version>
</parent>

也可以通過設置dependencyManagement的方式:

<properties>
    <!-- 框架版本配置-->
    <springboot-version>2.7.15</springboot-version>
    <springframework.version>5.3.29</springframework.version>
</properties>  

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>${springboot-version}</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>${springframework.version}</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

參考:

spring升級指南: https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions

springboot版本官網: https://spring.io/projects/spring-boot#learn

迴圈依賴問題

SpringBoot升級到2.7.15後,如果應用中存在迴圈依賴的問題,啟動時會報如下錯誤:

原因:官方文檔不鼓勵迴圈依賴引用,預設情況下是禁止的

解決方案:

第一種:推薦更新應用中bean的依賴關係來解決

第二種:配置文件中加入以下配置,為了和舊版本保持一致,此配置推薦添加

#放開迴圈依賴
spring.main.allow-circular-references=true

6.4、常用中間件升級

6.4.1、Lombok版本升級到1.18.20以上

<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <version>1.18.20</version>
</dependency>

如果不升級,編譯時會報錯如下:

6.4.2、swgger問題,springfox3.0.0和springboot2.7版本不相容

異常:

Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException: 
Cannot invoke "org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()" because "this.condition" is null

解決方案:

/**
 * 增加如下配置可解決Spring Boot 2.7.15 與Swagger 3.0.0 不相容問題
 **/
@Bean
public BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {

@Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
            }
return bean;
}

private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
            List<T> copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null).collect(Collectors.toList());
            mappings.clear();
            mappings.addAll(copy);
        }

@SuppressWarnings("unchecked")
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
try {
                Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
            } catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
            }
        }
    };
}

參考:https://developer.aliyun.com/article/950787

6.4.3、AKS升級(針對直接從JDK8升級的情況)

異常:Causedby: java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

原因:Java11 刪除了 Java EE modules,其中就包括 java.xml.bind (JAXB)。

解決方案:

手動引入如下包即可

<!-- API, java.xml.bind module --> 
<dependency>
      <groupId>jakarta.xml.bind</groupId>
      <artifactId>jakarta.xml.bind-api</artifactId>
      <version>2.3.2</version>
</dependency> 
<!-- Runtime, com.sun.xml.bind module -->
<dependency>
       <groupId>org.glassfish.jaxb</groupId>
       <artifactId>jaxb-runtime</artifactId>
       <version>2.3.2</version>
</dependency>

6.4.4、Concrete配置中心阻塞升級

使用 Concrete時,啟動時異常:

 Unable to make field private static final java.lang.reflect.Method jdk.proxy2.$Proxy97.m0 accessible: 
 module jdk.proxy2 does not "opens jdk.proxy2" to unnamed module @61d47554

原因:

分析下Concrete報錯的原因,如下圖,包內com.wangyin.concrete.spring.ConcreteConfigProcessor#postProcessAfterInitialization(212行)的實現邏輯

解決方案:

1、在JVM啟動參數中設置--add-opens jdk.proxy2來開啟私有欄位的訪問,但因為動態代理生成的包名是隨機不明確的,所以這種方案不可行。JDK官方文檔也明確表示不支持訪問動態代理內部的隨機欄位。官方說明:https://cr.openjdk.org/~mr/jigsaw/spec/api/java/lang/reflect/Proxy.html

2、代碼修改,只需把 f.setAccessible(true) 移到 Modifier.isStatic(f.getModifiers()) 的判斷下方即可。原因是方法 Modifier.isStatic(f.getModifiers()) 本來就要跳過靜態欄位,這樣修改直接避免了訪問。推動concrete團隊修複問題或更換使用Ducc配置中心

6.5、JVM啟動參數配置

6.5.1、開啟ZGC

啟動參數中配置:-XX:+UseZGC

移除-XX:ConcGCThreads,行雲部署下JVM參數配置需要清除

6.5.2、不同中間件所需啟動參數

升級JDK17後,項目啟動時可能會遇到如下兩種類型的異常:

1、cannot access class sun.util.calendar.ZoneInfo (in module java.base) because module java.base does not export sun.util.calendar to unnamed module @0x2611f533

2、Unable to make field final int java.math.BigInteger.signum accessible: module java.base does not "opens java.math" to unnamed module @525f1e4e

異常原因:

自從JDK9中引入了模塊化功能後,再到JDK17,對於包掃描和反射的許可權控制更加的嚴格。常見的庫比如(Spring)大量用到包掃描和反射,所以常出現此錯誤。

解決方案:

一個粗暴的解決辦法是將沒開放的module強制對外開放,即保持和Java9之前的版本一致。

  • --add-exports導出包,意味著其中的所有公共類型和成員都可以在編譯和運行時訪問。
  • --add-opens打開包,意味著其中的所有類型和成員(不僅是公共類型)都可以在運行時訪問。

主要區別在於--add-opens允許“深度反射”,即非公共成員的訪問,才可以調用setAccessible(true)

參考: https://stackoverflow.com/questions/44056405/whats-the-difference-between-add-exports-and-add-opens-in-java-9

SGM需要加入:

--add-opens java.management/java.lang.management=ALL-UNNAMED 
--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED 
--add-opens java.management/sun.management=ALL-UNNAMED

R2M需要加入:

--add-opens java.base/java.time=ALL-UNNAMED

Ducc需要加入:

--add-opens java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/java.util.concurrent.locks=ALL-UNNAMED
--add-opens java.base/java.security=ALL-UNNAMED
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED 
--add-opens java.base/java.net=ALL-UNNAMED 
--add-opens java.base/sun.nio.ch=ALL-UNNAMED 

AKS需要加入:

--add-exports java.base/sun.security.action=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/sun.util.calendar=ALL-UNNAMED

6.6、啟動後的驗證

1.推薦先升級JDK11,再到JDK17,一邊升級一邊進行驗證觀察

2.觀察日誌是否有異常,特別是上面說到的啟動時異常

3.觀察監控類軟體,比如SGM、UMP等監控是否正常

4.推薦逐步有序切量,並做好常態化壓測,防止影響核心業務

5.升級完成後,最好能做個全流程的功能測試,防止功能異常

7、總結

1、升級後,除了可以使用新的語法特性,最大的亮點是可以使用亞毫秒級停頓的GC性能(至少百倍的GC性能提升),所以 強烈建議升級到JDK17

2、整個升級過程並不複雜,主要涉及到中間件版本的升級和啟動參數的配置

如果還停留在JDK8,推薦先升級JDK11,再到JDK17,具體升級步驟先參考我的上篇文章“JDK8升級JDK11最全實踐乾貨來了”,再參考本章中的升級步驟。

希望以上分享可以給大家帶來實際的幫助,升級過程中如果遇到問題,歡迎大家在評論區回覆。

作者:京東科技 曲振富

來源:京東雲開發者社區 轉載請註明來源


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • python的可變參數 方法定義 #*args是可以傳list類型的可變參數,**kwargs是可以傳dict的可變參數 def wrapper(*args, **kwargs): 使用示例 def foo(*args, **kwargs): print 'args = ', args print ...
  • 原子引用 帶版本號的原子操作! 解決ABA問題,引入原子引用(樂觀鎖思想) AtomicStampedReference類解決ABA問題 package org.example.cas; import java.util.concurrent.TimeUnit; import java.util.c ...
  • C++ 中 <iterator> <functional> <numeric> 庫好用的函數 泰褲辣! <iterator> 簡述:迭代器省代碼用的。 std::advance 記憶方法:advance-前進。 形如:advance(it, step),表示 it 迭代器自增 step 步。 實現類 ...
  • 寫在前面 接上文《Python學習 —— 初步認知》,有需要請自取:Python學習 —— 初步認知 在這篇文章中,我們一起深入瞭解Python中常用的內置數據類型。Python是一種功能強大的編程語言,它提供了多種內置數據類型,用於存儲和操作數據。這些數據類型包括數字、字元串、序列和映射等。熟練掌 ...
  • 賦值表達式(assignment expression)是Python 3.8新引入的語法,它會用到海象操作符(walrus operator)。 這種寫法可以解決某些持續已久的代碼重覆問題。a = b是一條普通的賦值語句,讀作a equals b,而a := b則是賦值表達式,讀作a walrus ...
  • 1.首先是準備施法材料 JDK的下載地址:https://www.oracle.com/java/technologies/downloads/ 然後選擇自己的想要的版本和英雄(系統) 選擇x64 Compressed Archive免安裝版本進行下載(解壓就用,免除瘋狂確認的煩惱) 解壓到某個位置 ...
  • 來源:blog.csdn.net/qq_44384533/article/details/112324224 之前紅包權益領取查詢的介面超時了,因為有用戶訂購的權益有點多 解決方案 用線程池+ FutureTask將1個查詢拆分成多個小查詢 選擇FutureTask是因為它具有僅執行1次run()方 ...
  • NullPointerException(空指針異常):當試圖調用實例方法或訪問實例變數時,對象引用為 null 時拋出。ArithmeticException(算術異常):當試圖做出違反算術規則的操作時拋出,比如除以零。ClassCastException(類轉換異常):當試圖將對象強制轉換為不是... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...