2021-05-12 14:32:11
生產環境 JDK6 升級至 JDK8
由於 Oracle 已經不對 JDK6 和 JDK7 進行支援,同時為了利用 G1 收集器。所以我們在生產環境中,將專案從 JDK6 升級至 JDK8,並將垃圾收集器由 CMS 換成了 G1。下面對這次升級作一個總結,並且給出一些大家可能需要用到的資源。
升級指引
升級前首先需要了解一下 Oracle 對 JDK 各個版本的支援時間,JDK6, JDK7 分別於 2013, 2015 年停止了公開更新(public update),而 JDK8 將於 2017 年,或者更晚的時間,停止公開更新,詳細內容可以參考 Oracle Java SE Support Roadmap。
Java 版本之間是有二進位制向後相容性的,即 JDK8 的虛擬機器可以執行由 JDK7, JDK6 編譯的 class 檔案。如果有例外的話,Oracle 會在升級中明確說明。但是如果用 JDK8 了,還將程式碼編譯成之前版本的類檔案,就意味著無法使用 JDK8 中的新語法特性。Oracle 在大版本之間的升級,都會提供相容性指南,例如 Compatibility Guide for JDK 8,Java SE 7 and JDK 7 Compatibility。相容性指南包含的主要內容包括:
- 二進位制相容性
- 原始碼相容性
- 行為相容性
- 類檔案變化
- Java SE 與上一版本相比不相容的地方
- JDK 與上一版本相比不相容的地方
- Java SE 中移除的特性
- JDK 中移除的特性
- 不建議使用的 API
二進位制相容性
JDK7 編譯的程式碼可以在 JDK8 下執行,JDK8 下編譯的程式碼不可以在之前版本的 JDK 中執行 。
原始碼相容性
使用了 JDK8 語法新特性的原始碼,不能在之前版本的 Java 平台上編譯。不建議使用的 API 在用新 javac
編譯時,會給出警告,除非通過 -nowarn
關閉。sum.*
下的 API
有所變化,所以,一定要記得不要使用這個包下的 API
,不然升級會很痛苦。
行為相容性
行為相容性意味著同樣的輸入,有著同樣的行為。Java 平台中會有一些故意未指定的行為,在不同的 JDK 版本中,可能會有變化,所以程式碼不應該依賴於這些行為。如果 JDK 版本變了,行為也變了,可能說明程式碼裡有 bug。
類檔案
JDK8 編譯的類檔案,版本號為 52.0
。
JDK 及 Java SE 與上一版本的不相容性
這一部分內容比較多,如果從 JDK7 升級至 JDK8,可以參考 Compatibility Guide for JDK 8;如果從 JDK6 升級至 JDK8,需要同時參考 Compatibility Guide for JDK 8 和 Java SE 7 and JDK 7 Compatibility。
這一部分內容裡包含了原始碼不相容性,例如原來在 JDK7 下可以編譯的程式碼,在 JDK8 下就不能編譯了。這類錯誤還是比較好查的,編譯一下就可以找出這類錯誤。
或者是一些 GC 選項會被忽略,例如 PermSize
。
或者是類檔案中新增了一些新內容,例如 StackMapTable ,在執行時會對類檔案的這個欄位進行驗證,一些可能會調整位元組碼的工具,需要更新這個欄位。例如程式碼覆蓋率統計工具,這些工具如果調整了位元組碼,又沒有對 StackMapTable 進行更新。那麼高版本的 JDK 就無法執行經過調整的位元組碼,在位元組碼校驗階段就會失敗。
或者是同一份程式碼,由於 JDK8 引入的特性,生成不同位元組碼,最終導致 JDK8 下無法正常執行。這類錯誤通常只會在執行時才體現出來,比較難以察覺。例如我在這篇 JDK8 中的型別推斷與過載解析 中描述的這類情況。對這類情況,我們應該做到:
- 平時多寫測試用例
- 用 tcpcopy 引線上流量打壓
這樣,可以讓執行時錯誤或者行為不相容性更早地暴露出來
GC
從 JDK6 向上升級,在 GC 方面比較重要的就是 G1 這個垃圾收集器。G1 垃圾收集器在 JDK6u14 中作為實驗特性發布,在 JDK7 中正式發布。未來,在 JDK9 中,G1 將會作為預設的垃圾收集器,可見 G1 已經達到了比較成熟的階段。所以在 JDK8 中完全可以使用 G1。
而 G1 中比較重要的特性包括:
- 同時實現了新生代和老年代兩種 GC 演算法
- 各代之間不再有物理隔離,虛擬機器管理的記憶體分為多個 region
- 通過設定 GC 停頓的期望時間來調優(
-XX:MaxGCPauseMillis
)
使用建議包括:
- 適用於管理大記憶體的場景
- 適用於對延遲要求高於吞吐的場景
- 總是將詳細的 GC 紀錄檔記錄在 log 中
- 總是開啟
-XX:+ParallelRefProcEnabled
選項,實際環境中發現處理參照的過程確實也比較耗時 - 避免 Humongous 區域,即單個物件大小大於 region 區大小的 50%
- 盡力避免 Full GC
注意事項包括:
- 不要再設定新生代大小了,通過調整停頓時間,G1 會自動調整新生代大小
- 停頓時間只是期望時間,並不保證 STW 一定在這個時間內完成。根據經驗,如果將停頓時間設定為 x, 那麼 50% 的 GC 會在 x ms 內完成
在此次升級過程中,我看過的最重要的資料是一份 presentation: G1 Garbage Collector Details and Tuning。其中對 G1 中的重要特性,原理,使用建議,參考資料進行了說明,強烈建議閱讀。
除此之外,還有幾個和 G1 相關的部落格可以參考:
第三方庫
升級 JDK8 的過程中,一些第三方庫也需要同步更新。比較重要的一個原因在於,一些庫是依賴類檔案格式的,如果把程式碼編譯到 JDK8 的版本,那麼這些庫也需要同步更新。例如:
- 程式碼覆蓋率工具:Emma 不支援 JDK8,可以選用 Jacoco
- Spring: 4.x 版本才全面支援 JDK8,之前的版本可能無法正確讀取類檔案。可以參考 How Spring achieves compatibility with Java 6, 7 and 8。
同時,也需要注意,第三方庫的升級,也會帶來另外一些問題。例如,我們在升級 Spring 的過程中,就遇到了死鎖問題。
參考資料
- Upgrading major Java versions
- Compatibility Guide for JDK 8
- Java SE 7 and JDK 7 Compatibility
- G1 Garbage Collector Details and Tuning
- Spring Framework 4.0 M1 & 3.2.3 available
在CentOS7上安裝JDK1.8 http://www.linuxidc.com/Linux/2016-06/132678.htm
CentOS 搭建JDK環境 http://www.linuxidc.com/Linux/2015-06/118879.htm
CentOS6.3安裝JDK和環境設定 http://www.linuxidc.com/Linux/2012-09/70780.htm
本文永久更新連結地址:http://www.linuxidc.com/Linux/2016-07/133489.htm
相關文章