阿里妹導讀:Java 誕生於20年前,擁有大量優秀的企業級框架,踐行 OOP 理念,更多體現的是嚴謹以及在長時間運行條件下的穩定性和高性能。反觀現在,在要求快速迭代交付的雲場景下,語言的簡單性彷佛成了首要的要求,而傳統的 Java 語言顯得有一些過於重量了。今天,阿里 JVM 團隊技術專家鬱磊(花名:梁希)分享 JVM 團隊是如何面對和處理集團巨大的業務規模和複雜的業務場景的。
音樂無國界,可是音樂人有國界。web
雲原生亦如此。雖沒有限定的編程語言,但應用所使用的編程語言已經決定了應用部署運行的行爲。數據庫
Java 常由於耗資源而受詬病,其中最顯著一點就是 Heap 對內存的佔用,即使沒有請求在處理也沒有對象分配,進程仍然會保留完整的堆內存空間,保障 GC 進行分配內存和操做內存的快速敏捷。編程
AJDK ZenGC/ElasticHeap 雙十一全面支持核心鏈路上百應用和數十萬實例。緩存
JDK12 開始支持固定時間的觸發 concurrent mark 並在 remark 中收縮 Java 堆歸還內存的功能,然而並未解決在 stw 中增長暫停時間的問題,所以沒法在每次 young GC 時作內存歸還。ElasticHeap 在併發異步線程中完成內存處理反覆 map/unmap 以及 page fault 的開銷,所以任意一次 young GC 均可以敏捷的及時歸還內存,或從新恢復內存使用。架構
多個 Java 實例接受的流量任務較爲隨機,峯值不會重疊,在閒時能夠有效下降多個實例總體的內存佔用,提升部署密度。併發
雙11驗證核心交易系統使用 ElasticHeap 進行低功耗模式運行,大幅下降 WSS(Working Set Size) 規模的實例。框架
不少雲上的新應用不約而同地選擇了 Go 語言,很大的緣由是 Go 應用對運行時沒有依賴,靜態編譯的程序啓動速度快,也不須要經過 JIT 來預熱。在阿里有大量 Java 代碼的前提下,咱們是如何爲 Java 注入這方面的能力的呢?異步
Java 靜態編譯技術是一種激進的 AOT 技術,經過單獨的編譯階段將 Java 程序編譯爲本地代碼,在運行時無需傳統 Java 虛擬機和運行時環境,只需操做系統類庫支持便可。其工做基本原理以下圖所示。靜態編譯技術實現了 Java 語言與原生 native 程序的「合體」,將本來的 Java 程序編譯成爲了一個自舉的具備 Java 行爲的原生 native 程序,由此兼有 Java 程序和原生 native 程序的優勢。 編程語言
JVM 團隊與 SOFAStack 團隊密切合做,在中間件應用上率先實現靜態編譯的落地。將一個應用的啓動速度從 60 秒優化到 3.8 秒,雙十一期間靜態編譯的應用運行穩定,沒有故障, GC 停頓時間在 100 毫秒,在業務容許範圍以內,內存佔用和 RT 與傳統 Java 應用持平。 工具
綜上所述,靜態編譯的應用在穩定性、資源佔用、RT 響應等各方面指標與傳統 Java 應用基本持平的情況下,將啓動時間下降了 2000% 。
當你用時下最酷炫的 Vert.X 開發一個簡單的 Web 服務,準備體驗一下最強的性能, QA 同窗拿來一臺 1C 2G 的容器讓你壓一下,你卻發現你怎麼也拼不過別人 Go 應用。研究以後發現,原來協程模型在這樣的少核心的狀況下性能要好不少。是時代變了, Java 落伍了?
AJDK Wisp2 回答了這個問題:Java 一樣能夠擁有高性能的協程。今年是 Wisp2 大規模上線的第一年, Wisp2 具備以下特色:
在今年雙十一, Wisp 支持了上百應用,十萬級容器,其中 90% 的容器已經升級到 Wisp2 。
咱們能夠看到峯值附近, Wisp2 機器的 CPU 要低 7%( Wisp1 更低,Wisp2的取向是 RT ,所以 CPU 會高一些)左右,這主要是輕量級調度所節省的 sys CPU 。 0 點的 CPU 是相等的,這也說明一點:Wisp2 解決的是調度開銷,當 CPU 低,調度沒有壓力時是看不出差距的。
從 RT 角度看, Wisp2 機器的 RT 要低 20% 左右, RT 減小明顯的一個緣由是這批機器的 CPU 壓力很大,協程的調度優點更容易體現出來。這樣的優點能夠幫助系統摸高到更高的水位,總體地提升利用率而擔憂 RT 太高致使系統雪崩。
雙十一正零點相對後面幾分鐘會有一個明顯的 CPU 峯值,根據數據分析,主要緣由是雙十一零點觸發了 JIT 編譯。舉個例子,程序裏有邏輯:
if (is1111(LocalDate.now())) { branch1 } else { branch2 }
假設預熱時一直在走 branch2 ,那麼 JIT 有理由相信後續基本也都會走 branch2 ,而不會對 branch1編譯。在零點時,咱們進入 branch1 ,此時就須要觸發退優化從新編譯方法。咱們來看 AJDK 如何經過 profiling 解決這個問題。
JDK 運行代碼的時候,採用分層編譯的方式對 Java 方法進行動態編譯。在最高等級(峯值性能最好)的編譯中,出於性能的考慮,編譯的時候會根據收集的信息作一些比較樂觀的假設,一旦這些假設條件不知足了,就會出現退優化的現象。好比某個熱點方法中某段代碼僅會在雙十一中執行,那麼在預熱過程當中這段代碼不會被編譯,雙十一到來時這段代碼一旦被執行,就會觸發整個方法的退優化。
發生退優化有兩個方面的負面影響,一是須要運行的方法由高效率的編譯執行變成了解釋執行,運行速度下降百倍以上;二是流量高峯期退優化的方法會很快被從新編譯,編譯線程會消耗 CPU 。所以在雙十一這種流量短期劇增且與預熱流量不太同樣的場景下,退優化的危害會特別明顯。
FDO 是 feedback directed optimization 的縮寫,即參考以往 JVM 運行時的編譯信息,指導本次運行時進行更好的編譯。具體的,咱們採用了兩個層面的方法來減小退優化。
FDO 今年雙十一上線,目標解決兩個問題:
一、雙十一 0 點流量高峯和退優化/編譯高峯疊加形成的 CPU 使用率脈衝太高。
二、預熱效率低,壓測通過前長時間預熱後,增大流量時仍然伴隨着大量的編譯及退優化。
針對第一個問題,咱們收集了雙十一高峯第一分鐘的退優化/C2 編譯次數以及 CPU 數據。
可見開啓 FDO 後高峯期 C2 編譯數目減小約 45% ,退優化數目減小約 70% 。
CPU 數據上,高峯期第一分鐘內開啓 FDO 後 CPU 由約 67.5 下降到 63.1 ,下降約 7.0% 。
第二個目標能夠經過壓測第一分鐘的 CPU 數據驗證。
開啓 FDO ,壓測第一分鐘 CPU 使用率由 66.19 下降到 60.33% ,下降約 10% 。
ZProfiler 一直是全集團排查 Java 應用各種問題的利器,而 Grace 做爲其平臺化的版本,對其實施了一系列的優化,從原來的單機版本到如今的 Master/Worker 架構,同時引入了任務排隊機制,在高壓力狀況下對用戶的任務進行排隊從而解決 Worker 不堪重負的問題。在可維護性、拓展性、以及用戶體驗上獲得了質的提高,爲後續工具平臺的上雲、開源事項打下了夯實的基礎。
目前已經集成了 Heap Dump 功能,在繼承 ZProfiler 功能的基礎上作了必定的優化,提高了解析引擎的版本,支持更全面的 OQL 語法等等。
JDK11
JDK8 做爲一個經典版本,正被大規模使用,雖然從 JDK6 和 7 遷移上來有必定的陣痛,可是升級後廣泛的反饋是:「真香」。OpenJDK 8的下一個穩定版本是 OpenJDK 11 。JVM 團隊天然會在這個方向上積極跟進,目前 AJDK11 支持了 AJDK8 的 Wisp2 、多租戶特性。本次雙十一的部分集羣已經上線到 JDK11 ,表現穩定。升級 JDK11 是否會和升級 JDK8 同樣給咱們帶來一樣的的驚喜呢?在 JDK11 上咱們能夠體驗到最新的 ZGC 。
JDK11 引入了一個重要特性:ZGC 內存垃圾回收器。這個垃圾回收器號稱可以在幾十 GB 至若干 TB 的堆上把暫停時間保持在 10ms 之內。許多 Java 開發者苦於過去的垃圾回收器的暫停時間帶來延遲, ZGC 短暫停的特性將來無疑會成爲 Java 開發者的新寵。
目前 ZGC 在 OpenJDK 中仍然處於實驗特性,並且 JDK11 還沒有在產業界徹底普及, JDK11 只支持 Linux 上的 ZGC( MacOS 和 Windows 的 ZGC 預計在 2020 年 3 月發佈的 JDK14 版本纔會支持),許多 Java 開發者仍然只能垂涎欲滴,處於觀望狀態。
向來勇於吃螃蟹的咱們豈能望而卻步?阿里 JVM 團隊和數據庫團隊已經開始讓數據庫應用運行在 ZGC 上,並根據運行的效果對 ZGC 進行了相應的改進工做,包括 ZGC 的頁緩存機制優化、ZGC的觸發時機優化等等。
從 9 月開始,兩個團隊推進線上數據庫應用在 ZGC 上運行,目前已經穩定運行兩個月,並順利經過雙十一大考。線上反饋的效果可喜可賀:
一、 JVM 暫停時間保持在官方的 10ms 之內;
二、 ZGC 大大改善了線上運行集羣的平均 RT 與毛刺指標。
從上述的功能特性能夠看到 AJDK 已經從一個傳統的 Managed Runtime 脫胎換骨。從此 AJDK 將繼續致力於提升雲上的應用的開發體驗,經過底層的創新爲上層應用提供更多的可能。
本文做者:鬱磊
本文來自雲棲社區合做夥伴「阿里技術」,如需轉載請聯繫原做者。