依然順滑!Dragonwell 11如何改造全新垃圾回收器ZGC? | 龍蜥技術
本文是 Alibaba Dragonwell ZGC 系列的第三篇技術分享,重點介紹我們在 Dragonwell 11 上對 ZGC 的生產就緒改造工作,從而有效應對了本系列第一篇提到的 OpenJDK 11 實驗性 ZGC 的風險。文末將小結 Dragonwell ZGC 系列文章,並展望未來發展方向。
相關閱讀:
絲般順滑!全新垃圾回收器 ZGC 初體驗 | 龍蜥技術
中篇|絲般順滑!全新垃圾回收器 ZGC 原理與調優
ZGC生產就緒改造
本系列第一篇文章 曾提到阿里巴巴將 Dragonwell11 上的 ZGC 改造為生產就緒版本,同時也提到 OpenJDK12-16 並非長期支援版本導致難以在生產中大規模部署。那麼問題來了,為何不用 OpenJDK 11 的 ZGC,而需要用Dragonwell 11 的呢?Dragonwell 11 的 ZGC 有什麼具體的適用場景?
我們認為,只要採用 Java 11,那麼您就應該選擇 Dragonwell 11 的生產就緒 ZGC,而不是 OpenJDK 11 的實驗性 ZGC。其中最重要的理由,就是 實驗性 ZGC 有概率發生無徵兆的崩潰現象 (參考本系列第一篇文章),而該問題在生產就緒 ZGC 中得以修復。Dragonwell 11 的 ZGC 還完善了許多功能,讓 ZGC 能夠解鎖更多的場景。
Dragonwell 11 的生產就緒 ZGC 有諸多優勢,包括:
-
ZGC 功能完善且能解決實踐中遇到的問題; -
保持 Java 11 長期支援的質量穩定性;
-
完整的開源和測試流程。
ZGC 功能完善
ZGC 重構 C2 讀屏障
ZGC 多平臺支援
ZGC 類解除安裝支援
ZGC 記憶體使用優化
Dragonwell 11 ZGC 相比於 OpenJDK 11 增加了歸還實體記憶體、記憶體規格的擴充套件、並行 pre-touching 的支援。這些新增的支援能夠幫助 Dragonwell 11 ZGC 解鎖更多細化的場景。
歸還實體記憶體: ZGC 的歸還實體記憶體功能適用於“同一個機器部署多個例項”的場景。Dragonwell 11 的 ZGC 可通過設定 ZUncommit 來開啟歸還實體記憶體的功能。開發者只要設定堆的上限 Xmx、下限 Xms 以及 SoftMaxHeapSize,Java 業務平時使用的堆大小將保持在 Soft Max Heap Size 左右。當有突發流量到來之時,Java 業務可以臨時擴大堆的大小,以應對突發流量;當突發流量過去了以後,還可以將暫時用不到的記憶體歸還給作業系統。
記憶體規格的擴充套件: Dragonwell 11 擴充套件了 ZGC 的適用記憶體規格,能夠支援 16TB 的超大堆和 8MB 的超小堆,使得同一個業務部署不同規格的機器更加方便。
並行pre-touching: GC 的 pre-touching 的能力(開啟-XX:+AlwaysPreTouch)可以讓業務的 RT 免遭業務剛啟動時記憶體 touch 的影響,而 JDK 11 中的 ZGC pre-touching 是單執行緒的,導致應用啟動時候需要消耗很長的時間(大堆的 pre-touching 過程可達到分鐘級別)。Dragonwell 11 並行化改造了 pre-touching 的過程,使得大堆業務的啟動速度得以提升。
ZGC 響應時間優化
這裡的響應時間是關於 非暫停因素 影響 RT P99/P999 的情況。
在 JDK 11 的 ZGC 實踐過程當中,我們可能會看到 Page Cache Flush。
[2019-09-05T14:14:04.242+0800] GC(10816) Page Cache Flushed: 28M requested, 28M(11424M->11396M) flushed [2019-09-05T14:14:04.248+0800] Page Cache Flushed: 32M requested, 32M(11928M->11896M) flushed [2019-09-05T14:14:04.259+0800] Page Cache Flushed: 32M requested, 32M(11912M->11880M) flushed [2019-09-05T14:14:04.271+0800] Page Cache Flushed: 32M requested, 32M(11878M->11846M) flushed [2019-09-05T14:14:04.276+0800] Page Cache Flushed: 32M requested, 32M(11846M->11814M) flushed ... (省略35個"Page Cache Flushed") [2019-09-05T14:14:04.462+0800] Page Cache Flushed: 32M requested, 32M(10596M->10564M) flushed [2019-09-05T14:14:04.467+0800] Page Cache Flushed: 32M requested, 32M(10564M->10532M) flushed [2019-09-05T14:14:04.471+0800] Page Cache Flushed: 32M requested, 32M(10522M->10490M) flushed [2019-09-05T14:14:04.477+0800] GC(10816) Page Cache Flushed: 32M requested, 32M(10490M->10458M) flushed
這是因為 ZGC 把堆劃分成若干個 ZPage (與 G1 的 Region 概念相同), 包括小型 (2MB), 中型 (32MB), 大型 (2*N MB)三種規格 。物件分配時會把物件按照大小分配到相應規格的 ZPage 當中。Page Cache 是存放空閒 ZPage 的資料結構。
我們在實際執行當中遇到一個問題,即不同規格物件分配速率不穩定。有時候中型物件更多,那麼就會導致中型 ZPage 變少,需要把小型/大型 ZPage 轉化成中性 ZPage。這個轉化動作就是 Page Cache Flush。Page Cache Flush 耗時較長,需要多次進行 mmap 系統呼叫(開銷較大);Page Cache Flush 影響面大,需要鎖住 ZPage 分配全域性鎖。
Dragonwell 11 的解決辦法是 移植“提升 ZPage 分配併發度”的特性 。這個特性可以儘可能避免使用 ZPage 分配全域性鎖,並且非同步執行 mmap。Dragonwell 11 的另一個解決辦法是 調整中型 ZPage 的物件大小閾值 (原來的範圍:256KB~4MB),我們新增支援設定 ZMediumObjectUpperBound ,例如-XX:ZMediumObjectUpperBound=10MB (代表調整後中等 ZPage 的範圍:256KB~10MB)。實踐表明,Dragonwell 11 可以大幅減少了 Page Cache Flush 引發的執行緒阻塞,從而優化 RT P99/P999。
ZGC 吞吐率問題處理
ZGC 在生產實踐中有概率遇到吞吐率不足的情形,包括兩種現象:分配暫停 Allocation Stall 和記憶體不足 OOM(Out of Memory)。
現象1: 分配暫停 Allocation Stall (回收速度跟不上分配速度)
開發人員增加堆大小(Xmx)或併發 GC 執行緒數量( ConcGCThreads )可以緩解這一現象。然而機器的計算資源是有限的,不可能無限制地增加堆和執行緒數。這時候就要 考慮 ZGC 的觸發時機:
(1) ZAllocationSpikeTolerance: 這是 ZGC 在 JDK11 中就已經支援的,增加該引數可以處理分配速率毛刺,但是增加該引數不適應日常情形,過度觸發 ZGC 導致 CPU 消耗過高;
(2) ZHighUsagePercent: 一些業務對接的線上監控在堆的水位過高時候會報警。Experimental ZGC 對 ZGC 水位並沒有絕對的限制。Product ready ZGC設定了 95% 作為堆的最高水位。Dragonwell 11 可以通過 ZHighUsagePercent 調節堆最高水位,當堆水位超過ZHighUsagePercent%時觸發ZGC。
現象2: 記憶體 不足 OOM
ZGC 預留了固定的空間作為物件轉移的區域,但是如果Java執行緒訪問物件速度過快,就可能導致物件轉移速度過快,預留空間依然不足,最終導致 OOM,程式崩潰。
ZGC 監控升級
Dragonwell 11 更新了 GC 日誌的細節:包括錯誤活躍物件資訊更正,並顯示不同規格 ZPage 的統計資訊。
保持質量穩定性
阿里巴巴 Dragonwell 11 有選擇地移植生產就緒 ZGC 程式碼,並且對這部分程式碼 進行合理地改造 ,使得 Dragonwell 11 既擁有 OpenJDK15 的 ZGC 能力,也能夠享受到 OpenJDK11 長期支援的質量穩定性 。
-
後續升級困難:Dragonwell 11 會定期同步上游最新的 OpenJDK11 的程式碼,如果 OpenJDK11 的更新與我們的 Dragonwell 11 ZGC 改造同時修改了這部分程式碼,那麼這部分程式碼將難以維護,增加程式碼出錯的風險。
-
影響 Dragonwell 11 的其他部分程式碼的正確性:ZGC 依賴的公共程式碼改動,包括一些類載入和 C2 公共程式碼的改動。其他的 GC(包括 G1/CMS等)乃至 JDK 的其餘部分事實上也呼叫了這部分程式碼。如果沒有仔細移植公共程式碼改動,確認這些改動不會影響正確性,那麼使用者可能遭遇意想不到的風險。
因此我們需要對程式碼風險進行控制,把生產就緒改造儘可能控制在 ZGC 程式碼的範圍之內,選擇與生產就緒最相關的 ZGC 程式碼進行合理改造。
為了把改動控制在 ZGC 程式碼範圍之內,我們採用了編譯時檢查和執行時檢查的方式,保證 ZGC 改造程式碼不會“汙染”公共程式碼。(這部分工作參考了 Shenandoah GC Backport to JDK11 的工作)
編譯時檢查採用“巨集隔離”的方式 ,在關閉 ZGC 編譯時,程式碼不會被編譯,從而確保程式碼沒有問題。這樣的做法可以保證 ZGC 開啟編譯時的程式碼質量。“巨集隔離”即採用巨集的方式進行隔離:
#if INCLUDE_… #endif ZGC_ONLY( … )
if (UseZGC) { … }
開源與測試流程
我們在 GitHub 開源了 ZGC 生產就緒改造的過程,記錄在了里程碑 ()中。里程碑囊括了兩百餘個 ZGC 相關 patch。每個 patch 都得到了阿里巴巴專家的精心 review。
我們維護了負責測試的 Nightly build 流水線,保證每晚都能夠在 x64 和 AArch64 平臺上正常編譯,並且開啟/關閉 ZGC 都能通過 OpenJDK 的測試。
展望
我們注意到 ZGC 的一些最新進展,可以進一步優化 ZGC 的效能,包括:
小結
Alibaba Dragonwell ZGC 系列從 GC 概念,談到 ZGC 及其適用場景,以及Dragonwell 11 對 ZGC 的生產就緒改造。這項工作維持了 Dragonwell 11 的穩定性,同時把 ZGC 升級到了生產就緒的 ZGC:修復了 ZGC 重大缺陷,新增支援 AArch64 平臺,以及眾多新功能的完善。Dragonwell 11 還新增了若干通用特性,適應阿里內部和雲上客戶的需求。
未來我們還將不定期更新 Alibaba Dragonwell ZGC 系列,分享我們使用 ZGC 的經驗,以及我們在 OpenJDK 的相關貢獻。
相關連結
Alibaba_Dragonwell_11.0.11.7:
~pliden/slides/ZGC-OracleDevLive-2020.pdf
現
DragonWell 已加入
龍蜥社群 (OpenAnolis )Java 語言與虛擬機器 SIG,同時龍蜥作業系統(Anolis OS )8 版
本支援 DragonWell 雲原生 Java ,歡迎大家加入社群 SIG,參與社群共建。
官網:
加入微信群:新增社群助理-龍蜥社群小龍(微信:openanolis_assis),備註【龍蜥】拉你入群;加入釘釘群:掃描下方釘釘群二維碼。歡迎開發者/使用者加入龍蜥社群(OpenAnolis)交流,共同推進龍蜥社群的發展,一起打造一個活躍的、健康的開源作業系統生態!
龍蜥社群(OpenAnolis) 是由 企事業單位、高等院校、科研單位、非營利性組織、個人等按照自願、平等、開源、協作的基礎上組成的非盈利性開源社群。龍蜥社群成立於2020年9月,旨在構建一個開源、中立、開放的Linux上游發行版社群及創新平臺。
短期目標是開發龍蜥作業系統Anolis OS作為CentOS替代版,重新構建一個相容國際Linux主流廠商發行版。中長期目標是探索打造一個面向未來的作業系統,建立統一的開源作業系統生態,孵化創新開源專案,繁榮開源生態。
龍蜥OS 8.4已釋出,支援x86_64和ARM64架構,完善適配Intel、飛騰、海光、兆芯、鯤鵬晶片。
歡迎下載:
加入我們,一起打造面向未來的開源作業系統!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70004278/viewspace-2843392/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一文了解JVM全部垃圾回收器,從Serial到ZGCJVMGC
- 新一代垃圾回收器ZGC的探索與實踐GC
- ☕[JVM技術指南](3)垃圾回收子系統(Garbage Collection System)之垃圾回收器JVM
- 龍蜥社群高效能儲存技術 SIG 11 月運營回顧 | 龍蜥 SIG
- 垃圾回收之CMS、G1、ZGC對比GC
- 從原理聊JVM(三):詳解現代垃圾回收器Shenandoah和ZGCJVMNaNGC
- 龍蜥開源Plugsched:首次實現 Linux kernel 排程器熱升級 | 龍蜥技術Linux
- Java11改進的垃圾回收器Java
- 技術門檻高?來看 Intel 機密計算技術在龍蜥社群的實踐 | 龍蜥技術Intel
- JVM 垃圾回收演算法和垃圾回收器JVM演算法
- GC垃圾回收器GC
- JVM垃圾回收器JVM
- 助力Koordinator雲原生單機混部,龍蜥混部技術提升CPU利用率達60%|龍蜥技術
- 【JVM】垃圾回收器總結(2)——七種垃圾回收器型別JVM型別
- eBPF 雙子座:天使 or 惡魔?| 龍蜥技術eBPF
- 如何透過 open-local 玩轉容器本地儲存? | 龍蜥技術
- 如何保證 Java 應用安全?標準答案來了 | 龍蜥技術Java
- 龍蜥利器:系統運維工具 SysAK的雲上應用效能診斷 | 龍蜥技術運維
- 龍蜥開源核心追蹤利器 Surftrace:協議包解析效率提升 10 倍! | 龍蜥技術協議
- 萬里資料庫加入龍蜥社群,打造基於“龍蜥+GreatSQL”的開源技術底座資料庫SQL
- 垃圾回收(三)【垃圾回收通知】
- 跨語言程式設計的探索 | 龍蜥技術程式設計
- golang 垃圾回收器如何標記記憶體?Golang記憶體
- ☕[JVM技術指南](2)垃圾回收子系統(Garbage Collection System)之常見的垃圾回收演算法JVM演算法
- ☕[JVM技術指南](4)垃圾回收子系統(Garbage Collection System)之G1垃圾收集器SATBJVM
- 理解JVM(七):垃圾回收器JVM
- 垃圾回收器總結(一)
- 十種GC垃圾回收器GC
- Java新的Z垃圾收集器ZGC介紹JavaGC
- JVM 低延遲垃圾收集器 Shenandoah 和 ZGCJVMNaNGC
- 垃圾回收(一)【垃圾回收的基礎】
- Linux雲端計算技術學習:跟蹤JAVA虛擬機器的垃圾回收LinuxJava虛擬機
- JVM系列(六) - JVM垃圾回收器JVM
- 深入探究JVM之垃圾回收器JVM
- 探索G1垃圾回收器
- 探索ParNew和CMS垃圾回收器
- JVM系列(六) – JVM垃圾回收器JVM
- 更快的JVM垃圾回收器:ShenandoahJVMNaN