重塑雲上的 Java 語言

音樂無國界,可是音樂人有國界。web

雲原生亦如此。雖沒有限定的編程語言,但應用所使用的編程語言已經決定了應用部署運行的行爲。數據庫

Java 誕生於20年前,擁有大量優秀的企業級框架,踐行 OOP 理念,更多體現的是嚴謹以及在長時間運行條件下的穩定性和高性能。反觀現在,在要求快速迭代交付的雲場景下,語言的簡單性彷佛成了首要的要求,而傳統的 Java 語言顯得有一些過於重量了。編程

本文由阿里巴巴 JVM 團隊技術專家鬱磊(花名:梁希)分享 JVM 團隊是如何面對和處理集團巨大的業務規模和複雜的業務場景的。緩存

ElasticHeap

Java 常由於耗資源而受詬病,其中最顯著一點就是 Heap 對內存的佔用,即使沒有請求在處理也沒有對象分配,進程仍然會保留完整的堆內存空間,保障 GC 進行分配內存和操做內存的快速敏捷。安全

AJDK ZenGC/ElasticHeap 雙十一全面支持核心鏈路上百應用和數十萬實例。服務器

JDK12 開始支持固定時間的觸發 concurrent mark 並在 remark 中收縮 Java 堆歸還內存的功能,然而並未解決在 stw 中增長暫停時間的問題,所以沒法在每次 young GC 時作內存歸還。 ElasticHeap 在併發異步線程中完成內存處理反覆 map/unmap 以及 page fault 的開銷,所以任意一次 young GC 均可以敏捷的及時歸還內存,或從新恢復內存使用。架構

ElasticHeap 阿里巴巴實戰

ElasticHeap場景1:可預測的流量高峯

ElasticHeap 場景 2 :單機運行多個 Java 實例

多個 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% 。

Wisp2

當你用時下最酷炫的 Vert.X 開發一個簡單的 Web 服務,準備體驗一下最強的性能, QA 同窗拿來一臺 1C 2G 的容器讓你壓一下,你卻發現你怎麼也拼不過別人 Go 應用。研究以後發現,原來協程模型在這樣的少核心的狀況下性能要好不少。是時代變了, Java 落伍了?

AJDK Wisp2 回答了這個問題: Java 一樣能夠擁有高性能的協程。今年是 Wisp2 大規模上線的第一年, Wisp2 具備以下特色:
在整個Java runtime中支持了協程調度,線程(好比 Socket.getInputStream().read() )阻塞會變成更輕量的協程切換。
徹底兼容 Thread API ,在開啓 Wisp2 的 JDK 中,Thread.start() 實際建立的是一個協程(輕量級線程),能夠類比 Go 只提供協程關鍵字 go 而沒有暴露線程接口;咱們一樣只提供建立協程的方式,應用能夠透明切換到協程。
支持 work stealing ,調度策略特別適合 web 場景,在高壓力下調度開銷極小。

在今年雙十一, Wisp 支持了上百應用,十萬級容器,其中 90% 的容器已經升級到 Wisp2 。

咱們能夠看到峯值附近, Wisp2 機器的 CPU 要低 7%( Wisp1 更低,Wisp2的取向是 RT ,所以 CPU 會高一些)左右,這主要是輕量級調度所節省的 sys CPU 。 0 點的 CPU 是相等的,這也說明一點: Wisp2 解決的是調度開銷,當 CPU 低,調度沒有壓力時是看不出差距的。

從 RT 角度看, Wisp2 機器的 RT 要低 20% 左右, RT 減小明顯的一個緣由是這批機器的 CPU 壓力很大,協程的調度優點更容易體現出來。這樣的優點能夠幫助系統摸高到更高的水位,總體地提升利用率而擔憂 RT 太高致使系統雪崩。

FDO

雙十一正零點相對後面幾分鐘會有一個明顯的 CPU 峯值,根據數據分析,主要緣由是雙十一零點觸發了 JIT 編譯。 舉個例子,程序裏有邏輯:

if (is1111(LocalDate.now())) {
branch1
} else {
branch2 
}

假設預熱時一直在走 branch2 ,那麼 JIT 有理由相信後續基本也都會走 branch2 ,而不會對 branch1編譯。在零點時,咱們進入 branch1 ,此時就須要觸發退優化從新編譯方法。咱們來看 AJDK 如何經過 profiling 解決這個問題。

退優化原理及其危害

JDK 運行代碼的時候,採用分層編譯的方式對 Java 方法進行動態編譯。在最高等級(峯值性能最好)的編譯中,出於性能的考慮,編譯的時候會根據收集的信息作一些比較樂觀的假設,一旦這些假設條件不知足了,就會出現退優化的現象。好比某個熱點方法中某段代碼僅會在雙十一中執行,那麼在預熱過程當中這段代碼不會被編譯,雙十一到來時這段代碼一旦被執行,就會觸發整個方法的退優化。

發生退優化有兩個方面的負面影響,一是須要運行的方法由高效率的編譯執行變成了解釋執行,運行速度下降百倍以上;二是流量高峯期退優化的方法會很快被從新編譯,編譯線程會消耗 CPU 。所以在雙十一這種流量短期劇增且與預熱流量不太同樣的場景下,退優化的危害會特別明顯。

經過 FDO 減小退優化

FDO 是 feedback directed optimization 的縮寫,即參考以往 JVM 運行時的編譯信息,指導本次運行時進行更好的編譯。具體的,咱們採用了兩個層面的方法來減小退優化。
將每次運行時的退優化信息記錄到文件中,下次運行時讀取這個文件,在決定是否作樂觀假設的時候參考文件中的信息作判斷,從而減小退優化的機率。

信息顯示出現最多的退優化與 if-else 相關,佔總數量的一半以上。咱們提供了一個方法根據以往出現 if-else 退優化的信息,關閉某個路徑上全部相關的樂觀假設。

雙十一中 FDO 的效果

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% 。

Grace

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 。

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 將繼續致力於提升雲上的應用的開發體驗,經過底層的創新爲上層應用提供更多的可能。

在 Dragonwell 上使用 AJDK 功能

上述的這些通過雙十一考驗的功能都將隨着 Dragonwell 陸續開源和交付到廣大用戶手中,敬請關注。

Alibaba Dragonwell 8 是一款免費的 OpenJDK 發行版。它提供長期支持,包括性能加強和安全修復。Alibaba Dragonwell 8 目前支持 X86-64/Linux 平臺,在數據中心大規模 Java 應用部署狀況下, 能夠大幅度提升穩定性、效率以及性能。Alibaba Dragonwell 8 是 OpenJDK 的下游( friendly fork ),使用了和 OpenJDK 同樣的 license。Alibaba Dragonwell 8 與 Java SE 標準兼容,用戶可使用 Alibaba Dragonwell 8 開發和運行 Java 應用程序。這次開源的 Alibaba Dragonwell 8 是阿里巴巴內部 OpenJDK 定製版 AJDK 的開源版本, AJDK 爲在線電商,金融,物流作告終合業務場景的優化,運行在超大規模的,1,000,000+ 服務器的阿里巴巴數據中心。

近期咱們正在緊密籌備 Alibaba Dragonwell 11 的 release 。 Dragonwell 11 是基於 OpenJDK 11 的 Dragonwell 發行版本,擁有更多特性,能夠更多地爲雲上場景賦能,模塊化更加清晰,並將得到長期的支持,所以推薦你們關注和適時升級。


本文做者:中間件小哥

閱讀原文

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索