JDK21:GC不斷改進,效能更上一層樓

👉 這是一個或許對你有用的社群
🐱 一對一交流/面試小冊/簡歷最佳化/求職解惑,歡迎加入芋道快速開發平臺知識星球。下面是星球提供的部分資料:
👉這是一個或許對你有用的開源專案
國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。
功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、CRM 等等功能:
  • Boot 倉庫:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 倉庫:https://gitee.com/zhijiantianya/yudao-cloud
  • 影片教程:https://doc.iocoder.cn
【國內首批】支援 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 雙版本 

隨著 2023 年秋季釋出的 JDK 21,現在有一個新的 LTS 版本可以進行基準測試並生成一些 GC 效能圖表。JDK 21 和自 JDK 17 以來的其他版本提供了一系列值得注意的功能,如虛擬執行緒、用於switch 的模式匹配和分代 ZGC。讓我們看看它的表現如何。

簡介

當在不同的 JDK 版本之間進行效能比較時,很難確定哪些功能帶來了某種效能提升。但很容易看出,自 JDK 8 以來,整個 Java 平臺在效能上已經顯著提升。在這篇文章中,我使用 SPECjbb® 20151 來展示效能的提升。這是一個眾所周知的標準基準測試,非常適合展示對GC的改進。這主要是因為該基準測試提供了兩個得分:
  • max-jOPS :原始吞吐量
  • critical-jOPS :在延遲約束下的吞吐量
GC 的改進將同時提高這兩個得分,但對於受延遲約束的得分的改進更加與 GC 的改變密切相關。基本上,較短的暫停將獲得更好的得分。對於原始吞吐量得分來說,JIT 和 Java 平臺的其他部分的改進也起到了作用。
在基準測試時沒有進行太多調優,但我設定了一個固定的堆大小為 16 GB,並啟用了大頁,並確保在執行基準測試之前將它們分頁。我希望結果能夠反映出即插即用的行為,但是像這樣的配置可以獲得公平一致的結果。
基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 影片教程:https://doc.iocoder.cn/video/

選擇 GC

Oracle 支援 4 種不同的 GC,它們都有不同的用途。在這篇文章中,我不包括 Serial GC,因為它不適用於我使用的基準測試。Serial GC 的主要重點是低開銷,主要適用於記憶體和 CPU 資源有限的用例。在這次比較中,我們將重點介紹以下 GC:
  • G1 :自JDK 9起的預設收集器,注重延遲和吞吐量之間的平衡
  • Parallel :以吞吐量為導向的收集器,可能會出現較長的最壞情況延遲
  • Z :超低延遲的替代方案,完全併發,暫停時間在毫秒級別以下
使用哪種 GC 取決於應用程式最關注的方面。每種 GC 都有最佳的替代方案,並且沒有一種 GC 可以在所有用例中都表現最佳。
基於 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/yudao-cloud
  • 影片教程:https://doc.iocoder.cn/video/

進展

如果回顧 JDK 8 以來的進展,G1 和 Parallel 的改進在各個方面都是相當驚人的。這兩個收集器在各個方面都有所改進。它們的暫停時間更短,使用的記憶體更少,吞吐量比以往任何時候都要好。ZGC 的時間沒有那麼長,並且在這篇文章中,我主要關注透過使 ZGC 成為分代收集器帶來的改進。
本文中的圖表分別比較了不同的收集器。主要原因是根據堆大小的配置,結果對於某個收集器更有利。透過這樣做,我們可以關注所有 GC 的巨大進步,而不是試圖給出最佳的 GC。
比較包括 JDK 8、JDK 17 和 JDK 21 的 G1 和 Parallel。對於 ZGC,我選擇的三個資料點是 JDK 17、JDK 21 和 JDK 21 中的分代 ZGC。由於 JDK 17 是 ZGC 完全支援的第一個 LTS 版本,所以回顧更早的版本並沒有太多意義。

吞吐量

就原始吞吐量效能而言,與 JDK 17 相比,自 JDK 17 以來的改進並不是很大,但仍有輕微增加。但是,在下面的圖表中,有兩件事情真正值得關注。首先,JDK 8 和最新 JDK 之間的顯著差異對於G1和Parallel來說。從效能的角度來看,離開 JDK 8 從來沒有比現在更有益。

第二個值得注意的是使用分代 ZGC 時看到了有 10% 的提升。ZGC 中的新分代支援使其能夠更高效地回收記憶體,不需要考慮整個堆的每個 GC。其效果是在執行 GC 工作時消耗的 CPU 資源更少,這些資源可以用於應用程式,從而提高其效能。

延遲

對於延遲得分,情況基本上是一樣的。G1 和 Parallel 在 JDK 8 和 JDK 17 之間取得了巨大的進展,但最佳結果仍然是在 JDK 21。值得注意的是,在 JDK 8 和 JDK 17 之間進行了超過 7 年的創新,而在 JDK 17 和 21 之間,只有兩年。相比之下,較短的時間及 GC 的成熟,使得在這樣大型基準測試中很看到顯著的進展。

將分代引入 ZGC 仍然可以看到在分代 ZGC 和傳統模式之間的顯著差異,這非常好。值得注意的是,大部分的改進來自於吞吐量得分的提高。這兩種 ZGC 模式之間的暫停時間差異不大,它們都遠低於 1 毫秒。然而,當考慮最壞情況的延遲時,與傳統模式相比,分代 ZGC 相比之下顯得更好一些。

對於 G1 和 Parallel,暫停時間並沒有發生大的變化。我們在G1上花費了更多時間,在這裡,透過檢視更高百分位的暫停,我們可以看到我們已經成功地減少了幾毫秒的時間。

記憶體開銷

最後一個圖表比較了在固定負載下執行基準測試時的峰值本機記憶體開銷。就這個角度來看,Parallel 非常穩定,我們沒有花費任何時間來進一步最佳化它。另一方面,對於 G1,我們在過去的十年中已經成功地消除了許多低效性,並且在 JDK 20 中,我們將 G1 改為只需要一個標記點陣圖而不是兩個。由此節省下來的資源是顯著的,在這個基準測試中,G1 現在是最節省記憶體的收集器。

對於分代 ZGC,我們可以清楚地看到為了在這個基準測試中獲得更好的延遲和吞吐量而做出的權衡。代價是更高的本機記憶體消耗。為了有效地實現分代支援,我們需要跟蹤從老年代到新生代的指標。這稱為記憶集,並且它們佔用記憶體。在處理多個代時,我們還需要一些其他元資料所需的記憶體。儘管如此,在大多數情況下,與傳統 ZGC 相比,使用分代 ZGC 時總體記憶體消耗更低,因為它不需要處理給定工作負載所需的堆的大小。因此,額外的本機記憶體使用通常可以透過使用較小的堆來節省,並且仍然可以獲得更好的整體效能。

升級並嘗試分代 ZGC

就像上面看到的那樣,與 JDK 8 相比,JDK 21 的效能顯著提高。因此,如果你還在使用 JDK 8,應該開始考慮升級。在升級時,同時重新評估要使用的 GC 也是一個很好的時機。如果遷移到 JDK 21,我強烈建議嘗試使用分代 ZGC。在 JDK 21 中, ZGC 有分代版本和傳統模式都可用,要使用分代版本,需要指定下面兩個選項:
-XX:+UseZGC -XX:+ZGenerational

歡迎加入我的知識星球,全面提升技術能力。
👉 加入方式,長按”或“掃描”下方二維碼噢
星球的內容包括:專案實戰、面試招聘、原始碼解析、學習路線。

文章有幫助的話,在看,轉發吧。
謝謝支援喲 (*^__^*)

相關文章