<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Tencent Kona JDK11無暫停內(nèi)存管理ZGC生產(chǎn)實(shí)踐

          共 13506字,需瀏覽 28分鐘

           ·

          2021-06-22 01:35

          27e19fb0d10df6a08e5fca3e268f9ec1.webp

          騰訊大數(shù)據(jù) JVM 團(tuán)隊(duì)基于 OpenJDK11 自研的 Tencent Kona JDK11,目前已將 ZGC 特性孵化成熟,性能優(yōu)于 OpenJDK 所提供的版本,使 Java 能夠輕松構(gòu)建響應(yīng)時(shí)間在 ms 級(jí)別的強(qiáng)實(shí)時(shí)性在線服務(wù),極大提高研發(fā)和運(yùn)維效率,目前在騰訊內(nèi)部多業(yè)務(wù)場(chǎng)景生產(chǎn)落地,實(shí)現(xiàn)業(yè)務(wù)延遲 SLA 提升 2-3 個(gè)數(shù)量級(jí)。

          隨著 2021 年 4 月 30 日 Tencent Kona JDK 11.0.10-GA 正式對(duì)外發(fā)布,生產(chǎn)可用的 ZGC 也正式對(duì)外開源。


          43260fc43ccc205ea7de0393650edee6.webp

          背景

          經(jīng)過二十多年的發(fā)展,Java 語言的生態(tài)已經(jīng)龐大無比,應(yīng)用范圍覆蓋了從嵌入式設(shè)備到大型數(shù)據(jù)中心等場(chǎng)景,形成了各色各樣的業(yè)務(wù)形態(tài)。不同的業(yè)務(wù)關(guān)注點(diǎn)不盡相同,如部分離線應(yīng)用關(guān)注整個(gè)系統(tǒng)的吞吐率,而不太關(guān)注單個(gè)進(jìn)程的停頓時(shí)間;另外一些應(yīng)用則對(duì)于 GC 停頓的時(shí)間有嚴(yán)格的要求,比如以下業(yè)務(wù)形態(tài):

          1. 在線服務(wù)交互。和用戶交互的 UI 線程,需要按照特定的頻率進(jìn)行屏幕的刷新,比方說普通 60HZ 刷新率的屏幕,在播放動(dòng)畫時(shí),需要在 1s 內(nèi)刷新 60 次,才能保持屏幕畫面的連續(xù)性,即需要在 15ms 內(nèi)完成一次刷新,如果此時(shí)由于 GC 停頓導(dǎo)致 UI 線程掛起,則會(huì)導(dǎo)致畫面出現(xiàn)撕裂感,最終導(dǎo)致用戶體驗(yàn)的下降。

          2. 競(jìng)價(jià)廣告。在競(jìng)價(jià)廣告應(yīng)用場(chǎng)景下,如 Real Time Bidding 中的廣告競(jìng)價(jià)投放,一個(gè)廣告欄請(qǐng)求播放廣告時(shí),不同的廣告主則要根據(jù)當(dāng)前的用戶價(jià)值進(jìn)行交易競(jìng)價(jià),通常來說需要在規(guī)定的時(shí)間內(nèi)(一般為 100ms 到 200ms)達(dá)成交易,否則就會(huì)錯(cuò)失一次廣告曝光機(jī)會(huì),此時(shí) GC 停頓的控制就顯得非常重要。

          3. 量化交易。在交易機(jī)會(huì)出現(xiàn)時(shí),交易機(jī)構(gòu)需要以最快的速度達(dá)成交易,對(duì)于實(shí)時(shí)性的要求就更為嚴(yán)苛。如果出現(xiàn)由于 GC 停頓造成的延遲,輕則錯(cuò)失交易機(jī)會(huì),重則導(dǎo)致虧損。

          為了滿足不同的業(yè)務(wù)需求,Java 的 GC 算法也在不停迭代,對(duì)于特定的應(yīng)用,選擇其最適合的 GC 算法,才能更高效的幫助業(yè)務(wù)實(shí)現(xiàn)其業(yè)務(wù)目標(biāo)。對(duì)于這些延遲敏感的應(yīng)用來說,GC 停頓已經(jīng)成為阻礙 Java 廣泛應(yīng)用的一大頑疾,需要更適合的 GC 算法以滿足這些業(yè)務(wù)的需求。

          近些年來,服務(wù)器的性能越來越強(qiáng)勁,各種應(yīng)用可使用的堆內(nèi)存也越來越大,常見的堆大小從 10G 到百 G 級(jí)別,部分機(jī)型甚至可以到達(dá) TB 級(jí)別,在這類大堆應(yīng)用上,傳統(tǒng)的 GC,如 CMS、G1 的停頓時(shí)間也跟隨著堆大小的增長(zhǎng)而同步增加,即堆大小指數(shù)級(jí)增長(zhǎng)時(shí),停頓時(shí)間也會(huì)指數(shù)級(jí)增長(zhǎng)。特別是當(dāng)觸發(fā) Full GC 時(shí),停頓可達(dá)分鐘級(jí)別。當(dāng)業(yè)務(wù)應(yīng)用需要提供高服務(wù)級(jí)別協(xié)議(Service Level Agreement,SLA),例如 99.99% 的響應(yīng)時(shí)間不能超過 100ms,此時(shí) CMS、G1 等就無法滿足業(yè)務(wù)的需求。

          為滿足當(dāng)前應(yīng)用對(duì)于超低停頓、高 SLA 的需求,并應(yīng)對(duì)大堆和超大堆帶來的挑戰(zhàn),伴隨著 2018 年發(fā)布的 JDK 11,A Scalable Low-Latency Garbage Collector - ZGC 應(yīng)運(yùn)而生。騰訊大數(shù)據(jù) JVM 團(tuán)隊(duì)的 Tencent Kona JDK 作為 OpenJDK Hotspot VM 下游分支,也致力于在 LTS 的 JDK11 版本上提供 Production Ready 的 ZGC 功能,滿足公司內(nèi)部客戶的需求。


          43260fc43ccc205ea7de0393650edee6.webp

          GC 停頓

          1.停頓之由來

          在 Hotspot 虛擬機(jī)上,GC 算法均是基于 Mark Sweep 或者 Mark Compact 實(shí)現(xiàn)的,也可以稱為 Tracing GC。對(duì)于這類標(biāo)記掃描的 GC 算法來說,需要通過 Mark 找到所有活著的對(duì)象,然后將死對(duì)象清除,或者把所有的活對(duì)象拷貝到另外一塊區(qū)域,以達(dá)到清理內(nèi)存的目的。因此,所有的 Tracing GC 均需要以下三個(gè)步驟,其整體過程如下面動(dòng)圖所示:

          857e91f92b3d37a2847aa329a9b10366.webp

          • 找出所有的 GC Roots 集合:這是 Tracing GC 算法的起點(diǎn),GC Roots 主要為運(yùn)行時(shí)的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)中存放的指向堆對(duì)象的指針,如線程棧上的堆對(duì)象指針等。

          • 標(biāo)記過程:從 GC Roots 開始遍歷整個(gè)對(duì)象圖,找出所有存貨的對(duì)象。而剩余未被標(biāo)記的對(duì)象則為死對(duì)象。

          • 清理過程:將死掉的對(duì)象清理掉,釋放其占用的內(nèi)存。當(dāng)然清理時(shí)可以直接釋放對(duì)象內(nèi)存,也可以將所有的活對(duì)象移動(dòng)到一塊連續(xù)的區(qū)域里,并將原來的內(nèi)存空間釋放。

          可以看到,上面三個(gè)步驟均需要一致的信息,如 GC Roots 需要是完整的,不能在掃描時(shí)被隨意修改;標(biāo)記過程,需要掃描到所有活著的對(duì)象,其他線程不能隨意修改對(duì)象圖;清理過程如果需要搬移對(duì)象,則需要更新所有指向該對(duì)象的地方,如對(duì)象 A 指向 B,B 被搬動(dòng)之后,A 中的指針需要同步更新。因此要求在這三步中,采取同步措施,而最簡(jiǎn)單的同步措施就是暫停所有的 Java 線程,即 Stop-The-World(STW),在 STW 期間,GC 線程就可以安全的訪問各種運(yùn)行時(shí)數(shù)據(jù)、對(duì)象圖、更新對(duì)象指針等。如果需要降低 STW 的時(shí)間,則需要將 GC 的不同階段的任務(wù)移出 STW,和 Java 線程進(jìn)行并發(fā)執(zhí)行,這個(gè)時(shí)候就需要算法和數(shù)據(jù)結(jié)構(gòu)方面的更改,以滿足 GC 線程和 Java 線程對(duì)當(dāng)前 GC 的一致性。

          不同的 GC 算法實(shí)現(xiàn),其 STW 階段需要完成的任務(wù)大相徑庭,造成不同 GC 算法 STW 時(shí)長(zhǎng)的不同。所有的任務(wù)均在 STW 階段完成時(shí),這類 GC 就不需要和應(yīng)用線程搶占 CPU,從應(yīng)用整體來看,最終的吞吐率是比較高的,如 Parallel GC;而當(dāng) STW 階段的任務(wù)減少時(shí),則需要在并發(fā)階段增加相應(yīng)的任務(wù)——即部分 GC 任務(wù)需要和業(yè)務(wù)線程一起運(yùn)行,相互搶占 CPU,這類 GC 根據(jù)不同的任務(wù)劃分,最終在吞吐率和停頓之間達(dá)到一個(gè)平衡,如 CMS 和 G1 致力于以較小的吞吐率損失換取較小的停頓和較高的響應(yīng);ZGC 和 ShenandoahGC 則關(guān)注極致停頓,盡一切可能減少 STW 的工作量,從而實(shí)現(xiàn) ms 級(jí)別的停頓時(shí)間。

          2.業(yè)界解決方案

          為了與 Apple 的 iOS 系統(tǒng)進(jìn)行競(jìng)爭(zhēng),Google 主導(dǎo)的 Android 系統(tǒng)需要解決的一大問題就是顯示卡頓問題,通過對(duì) GC 算法的不斷演進(jìn),實(shí)現(xiàn)基于 Baker Barrier 的 Concurrent Copy GC 算法,停頓時(shí)間控制在幾個(gè) ms 級(jí)別,小于 15ms 的刷新約束,補(bǔ)全了 Java 在嵌入式設(shè)備中的短板,使得松散的 Java 生態(tài)能夠?qū)崿F(xiàn)和嚴(yán)格控制的 Apple 生態(tài)一樣流暢的系統(tǒng)。

          在 ZGC 和 ShenandoahGC 出現(xiàn)之前,Hotspot JVM 的 GC 停頓很難平穩(wěn)的控制在百毫秒以下,極大地阻礙了 OpenJDK 在金融行業(yè)等延遲敏感的場(chǎng)景的應(yīng)用。但是作為 OpenJDK 的下游分支,Azul 的 Zing 虛擬機(jī)憑借其閉源的 C4 GC,實(shí)現(xiàn)了近乎“無停頓”的低延遲,在前十幾年中大放異彩,頻繁出現(xiàn)在各類交易系統(tǒng)中。云上的各類系統(tǒng)和普通的桌面應(yīng)用,則面臨著無低延遲 GC 可用的窘境。為滿足業(yè)務(wù)需求,一般會(huì)采用 C++ 等 Native 語言重寫一些重要模塊,如騰訊的廣告系統(tǒng)采用 C++ 實(shí)現(xiàn),或者購置 Zing 等實(shí)現(xiàn)低延遲 GC 的 VM,抑或在 CMS 和 G1 上八仙過海、各顯神通,利用經(jīng)驗(yàn)調(diào)整參數(shù)來滿足基本的業(yè)務(wù)需求。

          Hotspot 上的停頓,導(dǎo)致備選方案面臨 C++ 的高開發(fā)門檻、經(jīng)費(fèi)成本和調(diào)參經(jīng)驗(yàn)等各種問題,以及大堆管理問題,使得停頓成為 Java 開發(fā)者心中的夢(mèng)魘,阻礙了 Java 在低延遲需求業(yè)務(wù)的應(yīng)用,此乃當(dāng)前 Hotspot JVM 上的停頓之殤、開發(fā)者之痛。為了解決這一問題,ZGC 采用了和 Azul 的 Zing VM 相似的 GC 算法,從 JDK11 開始開源孵化,直到 JDK15 補(bǔ)全各類功能,成為真正可以商用的正式版本,保證了 Java 停頓時(shí)間不會(huì)隨著堆大小和業(yè)務(wù)規(guī)模的增加而增長(zhǎng)。

          JDK11 在 2018 年下半年發(fā)布,是最新的 Long-Term Support 版本,而后續(xù) LTS 版本為 JDK17,將于 2021 年下半年發(fā)布,JDK12 到 JDK16 屬于中間過渡開發(fā)版本,不會(huì)像 JDK11 和 JDK17 一樣提供持續(xù)的更新和修復(fù)。ZGC 在 OpenJDK11 上屬于 Experimental 實(shí)驗(yàn)特性,無法滿足業(yè)務(wù)的商用需求,騰訊 JVM 團(tuán)隊(duì)為了提前滿足業(yè)務(wù)的需求,在 Tencent Kona JDK11 持續(xù)的更新和修復(fù)的同時(shí),將 ZGC 的各項(xiàng)功能補(bǔ)全,并進(jìn)行了長(zhǎng)期的驗(yàn)證落地,使得 Tencent Kona JDK11 上的 ZGC 能夠達(dá)到商用水平,讓停頓敏感的業(yè)務(wù)應(yīng)用在 JDK11 這個(gè) LTS 版本上實(shí)現(xiàn)超低 GC 延遲。


          43260fc43ccc205ea7de0393650edee6.webp

          ZGC 簡(jiǎn)介

          ZGC 是由提議 JEP 333(https://openjdk.java.net/jeps/333)引入 Hotspot Runtime,其目標(biāo)是為了徹底解決 GC 停頓帶來的延遲問題,總的設(shè)計(jì)目標(biāo)為:

          • 每次 GC 總的停頓時(shí)間控制在 10ms 以下

          • 相對(duì)于 G1,應(yīng)用的吞吐率降低不超過 15%

          • 支持大堆和特大堆(8MB~16TB),并且停頓時(shí)間不隨堆大小的增長(zhǎng)而增長(zhǎng)

          1a37ad812eb365c85161848fcade7d9c.webp

          由設(shè)計(jì)目標(biāo)可知,ZGC 主要是為現(xiàn)在及未來大堆的管理問題服務(wù),致力于以最小的性能損失換取最大的停頓優(yōu)勢(shì)。從 Oracle 發(fā)布的測(cè)試數(shù)據(jù)來看 (參見 [1]),上圖中 SPECjbb2015 上 ZGC 的吞吐率(max-JOPS)和 Parallel GC、G1GC 相差無幾,而體現(xiàn)停頓影響的指標(biāo) critical-JOPS 則提升了 20%+;在暫停時(shí)間上,ZGC 則不會(huì)超過 10ms,而 Parallel GC 和 G1GC 則高達(dá) 100ms+,如下圖所示。因此,ZGC 尤其適合對(duì)延遲比較敏感的大堆任務(wù)。

          fb6779e8d73ce77d67ef5bc5d07f581c.webp

          1. ZGC算法實(shí)現(xiàn)

          為減少停頓,需要減少 STW 中執(zhí)行的任務(wù),ZGC 主要在以下三個(gè)方面進(jìn)行推進(jìn):

          • GC Roots 的掃描。將能夠移除到 STW 以外的 Roots 掃描外移到并發(fā)階段,Roots 掃描的并發(fā)外移需要對(duì) Roots 的數(shù)據(jù)結(jié)構(gòu)進(jìn)行改造,以支持 GC 線程和 Java 線程同時(shí)操作。

          • Runtime 數(shù)據(jù)結(jié)構(gòu)的處理。在 Runtime 中維護(hù)了很多張表來記錄 Meta(class、method、jit code 等),并且 Java 存在一類特殊的弱引用,即 java.lang.ref.Reference 及其子類,需要額外處理。

          • 對(duì)象移動(dòng)的并發(fā)化改造。為了能夠讓移動(dòng)對(duì)象和 Java 線程同時(shí)運(yùn)行,需要增加 Read barrier 來保證每次對(duì)象 field 讀取的正確性。

          ZGC 在 GC 算法的處理邏輯上有很大的變更,但是在整體邏輯上,與其前輩 GC 算法一樣,都是 Mark&Compact 形式。具體實(shí)現(xiàn)上,ZGC 下面六個(gè)階段通過來實(shí)現(xiàn)低延遲的 GC 算法,如下圖所示:

          30cd0d07586a72fce86f24f39c2fdd7e.webp

          • 第一個(gè)階段是 Pause Mark Start:主要做一些全局狀態(tài)的設(shè)置和全局?jǐn)?shù)據(jù)結(jié)構(gòu)的初始化這類輕量化的任務(wù),標(biāo)明后續(xù)并發(fā)階段需要做 GC 的 Concurrent Mark。

          • 第二個(gè)階段是 Concurrent Mark & Remap:將耗時(shí)占比最大的 GC Roots 進(jìn)行并發(fā)化改造,支持并發(fā) Roots 標(biāo)記。從 GC Roots 進(jìn)行對(duì)象圖的并發(fā)標(biāo)記。上一輪 GC 的指針更新(Remap)通過 Piggyback,放到當(dāng)前階段執(zhí)行,從而減少對(duì)對(duì)象圖的遍歷。

          • 第三個(gè)階段是 Pause Mark End:這一階段做 Concurrent Mark 的同步,結(jié)束并發(fā)標(biāo)記階段,同時(shí)設(shè)置部分全局變量。

          • 第四個(gè)階段是 Concurrent Prepare:這一階段主要做 java.lang.ref.Reference 等弱引用的處理,并選擇出需要 Compact 的 ZGC Region。

          • 第五個(gè)階段是 Pause Relocate Start:這一階段和第三階段比較類似,主要是全局同步,設(shè)置全局變量,并指示 Relocate 階段的開始。

          • 第六個(gè)階段是 Concurrent Relocate:并發(fā)的搬移對(duì)象。

          相對(duì)于其他 GC,ZGC 需要三個(gè) STW 階段來做全局的同步,但每個(gè) STW 中的任務(wù)都很明確,需要完成的任務(wù)的時(shí)間和 CPU 的處理速度正相關(guān),因此可以做到 ms 級(jí)別的停頓。相對(duì)于 G1GC,ZGC 的難點(diǎn)在于如何進(jìn)行 GC Roots 的并發(fā)化改造和對(duì)象搬移的并發(fā)化改造。

          32d6c453f02bc72bd93b4a975f56a54d.webp

          對(duì)于對(duì)象搬遷的并發(fā)化改造,ZGC 則采用 Colored Pointer 來實(shí)現(xiàn)輕量級(jí)的 Read Barrier,如上圖所示。對(duì)于 64bit 的系統(tǒng),高位 bit 中拿出 4 個(gè) bit 來指示不同的處理狀態(tài),兩個(gè) Mark 位表明該對(duì)象指針是否已經(jīng)被標(biāo)記,采用兩個(gè) Mark bit 可以在前后不同的 GC 時(shí)使用不同的 Mark bit;Remapped 位表示當(dāng)前對(duì)象指針是否已經(jīng)調(diào)整為搬移之后的對(duì)象指針;Finalizable 位主要是為 Finalizable 對(duì)象服務(wù),用來表示該對(duì)象指針是否僅經(jīng) Finalize 對(duì)象標(biāo)記,主要供 Mark 階段和弱引用處理階段使用。通過 Colored 指針,不同的 GC 階段,當(dāng)前 Runtime 的正確的指針顏色僅為一種顏色 (Marked 或者 Remapped),就可以通過下圖所示,測(cè)試對(duì)象指針是否為 bad color 即可,在 x86 上最終實(shí)現(xiàn)為一條 test 指令和一條 jne 跳轉(zhuǎn)指令。

          790543a291751b8f409be09a274efe10.webp

          Colored Pointer 導(dǎo)致不同的時(shí)期,對(duì)象的指針的高位是不同,如下圖中的對(duì)象指針 0x0000000012345678,在程序運(yùn)行過程中,可能以下面三種狀態(tài)被 Java 線程感知到:Remapped 狀態(tài)、Mark1 狀態(tài)、Mark0 狀態(tài)。為了使得這幾種不同的狀態(tài)(不同值的指針),指向同一份對(duì)象,ZGC 完全利用了操作系統(tǒng)的虛擬地址和物理地址轉(zhuǎn)換,使得這三種狀態(tài)的虛擬地址指針指向同一份物理地址,因此 ZGC 的 Java 堆需要在虛擬地址中占用三份地址。ZGC 通過內(nèi)存文件來占用實(shí)際的物理內(nèi)存,然后將這個(gè)內(nèi)存文件映射到 Remapped、Mark0 和 Mark1 指向的虛擬地址。可以看出,雖然表面上 ZGC 的 Java Heap 占用了三份虛擬地址,但是實(shí)際的物理地址只有一份。這也是 linux 的命令 top 或者 ps 看到啟用 ZGC 的 Java 進(jìn)程 RSS 內(nèi)存膨脹三倍的原因,但開啟 ZGC 之后觀察到的 RSS 消耗并非實(shí)際物理內(nèi)存消耗。

          b3c4e51074c847c4c5f01485d75dc1b6.webp

          2. ZGC算法的開銷

          ZGC 對(duì)于業(yè)務(wù)線程的影響主要集中在以下五個(gè)方面:

          • Read barrier 的開銷。在 Java 程序中,對(duì)象指針的讀取次數(shù)要遠(yuǎn)超于對(duì)象指針的寫入次數(shù),Read Barrier 的插入點(diǎn)要遠(yuǎn)多于 Write Barrier 的插入點(diǎn),因此 ZGC 的 Read Barrier 會(huì)對(duì)程序的性能產(chǎn)生較大的負(fù)面影響。

          • JIT 方法的 entry barrier 開銷。如果 JIT 之后的代碼包含了已經(jīng)死掉的 java 對(duì)象,那么該方法就應(yīng)該丟棄掉,因此 JIT 的代碼需要在進(jìn)入時(shí)利用一個(gè) entry barrier 來保證自身和其包含的 meta 信息的有效性。ZGC 對(duì)每個(gè) JIT 代碼都生成 nmethod entry barrier,會(huì)對(duì) JIT 方法產(chǎn)生輕微的性能損失。

          • Frame barrier 開銷。為并發(fā)進(jìn)行 Java 棧幀的掃描,降低 Stack Roots 掃描對(duì) STW 時(shí)間的影響,當(dāng)前 Hotspot 采用 StackWaterMark 來進(jìn)行并發(fā)掃棧。同時(shí)為了降低業(yè)務(wù)線程掃描棧幀的工作量,Hotspot 中采用單個(gè)棧幀掃描的方式,即在回棧時(shí)如果超過當(dāng)前 stack water mark,就會(huì)陷入 stack mark barrier,修復(fù) caller 的 java 對(duì)象指針。參見 https://openjdk.java.net/jeps/376

          • 其他 Runtime 改造產(chǎn)生的鎖結(jié)構(gòu)帶來的開銷。

          • ZGC 中大部分的 GC 工作放在并發(fā)階段,因此并發(fā)階段 GC 線程和 Java 業(yè)務(wù)線程搶占 CPU,導(dǎo)致的對(duì)業(yè)務(wù)線程的搶占開銷。

          可以看出 ZGC 為了降低 STW 造成的停頓影響,采取的措施是極致的并發(fā)化改造,也就是以輕微的性能損失換取最低的停頓影響。當(dāng)前最新的 ZGC 實(shí)現(xiàn)停頓已經(jīng)達(dá)到 ms 級(jí)別,低于 Linux 內(nèi)核的背景噪聲,即調(diào)度開銷和系統(tǒng)調(diào)用開銷,也有可能造成 10ms 級(jí)別的影響,可以說 ZGC 使得 Java 不能服務(wù)實(shí)時(shí)業(yè)務(wù)的古板印象得到徹底的顛覆。


          43260fc43ccc205ea7de0393650edee6.webp

          ZGC 的使用和調(diào)參

          1. ZGC典型應(yīng)用場(chǎng)景

          此之蜜糖、彼之砒霜,不同的 GC 算法都有其長(zhǎng)短處,ZGC 出現(xiàn)的最大優(yōu)勢(shì)是能夠在保證停頓時(shí)間控制 10ms 以下,但為了實(shí)現(xiàn)這種高 SLA 的停頓時(shí)間,其代價(jià)是性能的損失和內(nèi)存消耗。從前面介紹可以看出,為了降低 STW 中的工作,很多 GC 任務(wù)做了并發(fā)化改造,而并發(fā)化改造的代價(jià)則散亂在各種運(yùn)行細(xì)節(jié)中,通過整個(gè) OpenJDK 社區(qū)的持續(xù)投入,當(dāng)前 ZGC 在性能損失場(chǎng)景中的性能下降已經(jīng)控制在很小的范圍內(nèi)。對(duì)于性能來說,不同的配置對(duì)性能的影響是不同的,如充足的內(nèi)存下即大堆場(chǎng)景,ZGC 在各類 Benchmark 中能夠超過 G1 大約 5% 到 20%,而在小堆情況下,則要低于 G1 大約 10%;不同的配置對(duì)于應(yīng)用的影響不盡相同,開發(fā)者需要根據(jù)使用場(chǎng)景來合理判斷。當(dāng)前 ZGC 不支持壓縮指針和分代 GC,其內(nèi)存占用相對(duì)于 G1 來說要稍大,在小堆情況下較為明顯,而在大堆情況下,這些多占用的內(nèi)存則顯得不那么突出。因此,以下兩類應(yīng)用強(qiáng)烈建議使用 ZGC 來提升業(yè)務(wù)體驗(yàn):

          • 超大堆應(yīng)用。超大堆(百 G 以上)下,CMS 或者 G1 如果發(fā)生 Full GC,停頓會(huì)在分鐘級(jí)別,可能會(huì)造成業(yè)務(wù)的終端,強(qiáng)烈推薦使用 ZGC。

          • 高 SLA 需求的應(yīng)用。如對(duì)響應(yīng)時(shí)間有 P999 時(shí)限要求的實(shí)時(shí)和軟實(shí)時(shí)應(yīng)用,此類應(yīng)用無論堆大小,均推薦采用低停頓的 ZGC。

          2. ZGC參數(shù)設(shè)置

          ZGC 之美不僅在于其超低的 STW 停頓,也在于其參數(shù)的簡(jiǎn)單,絕大部分生產(chǎn)場(chǎng)景都可以自適應(yīng)。當(dāng)然,極端情況下,還是有可能需要對(duì) ZGC 個(gè)別參數(shù)做個(gè)調(diào)整,大致可以分為三類:

          • 堆大小:Xmx。ZGC 能夠通過極致的低延遲滿足業(yè)務(wù)高標(biāo)準(zhǔn) SLA 的服務(wù)準(zhǔn)入條件,但是與所有編程語言的 concurrent GC 類似,延遲是以內(nèi)存空間作為 trade-off 的。當(dāng)分配速率過高,超過回收速率,造成堆內(nèi)存不夠時(shí),會(huì)觸發(fā) Allocation Stall,這類 Stall 會(huì)減緩當(dāng)前的用戶線程。因此,當(dāng)我們?cè)?GC 日志中看到 Allocation Stall,通常可以認(rèn)為堆空間偏小或者 concurrent gc threads 數(shù)偏小。

          • GC 觸發(fā)時(shí)機(jī):ZAllocationSpikeTolerance, ZCollectionInterval。ZAllocationSpikeTolerance 用來估算當(dāng)前的堆內(nèi)存分配速率,在當(dāng)前剩余的堆內(nèi)存下,ZAllocationSpikeTolerance 越大,估算的達(dá)到 OOM 的時(shí)間越快,ZGC 就會(huì)更早地進(jìn)行觸發(fā) GC。ZCollectionInterval 用來指定 GC 發(fā)生的間隔,以秒為單位觸發(fā) GC。

          • GC 線程:ParallelGCThreads, ConcGCThreads。ParallelGCThreads 是設(shè)置 STW 任務(wù)的 GC 線程數(shù)目,默認(rèn)為 CPU 個(gè)數(shù)的 60%;ConcGCThreads 是并發(fā)階段 GC 線程的數(shù)目,默認(rèn)為 CPU 個(gè)數(shù)的 12.5%。增加 GC 線程數(shù)目,可以加快 GC 完成任務(wù),減少各個(gè)階段的時(shí)間,但也會(huì)增加 CPU 的搶占開銷,可根據(jù)生產(chǎn)情況調(diào)整。

          由上可以看出 ZGC 需要調(diào)整的參數(shù)十分簡(jiǎn)單,通常設(shè)置 Xmx 即可滿足業(yè)務(wù)的需求,大大減輕 Java 開發(fā)者的負(fù)擔(dān)。當(dāng)前 Tencent Kona JDK11 上開啟 ZGC 的參數(shù)為:“-XX:+UnlockExperimentalVMOptions -XX:+UseZGC”。


          43260fc43ccc205ea7de0393650edee6.webp

          ZGC 生產(chǎn)注意事項(xiàng)

          1.  RSS 內(nèi)存異常現(xiàn)象

          由前面 ZGC 原理可知,ZGC 采用多映射 multi-mapping 的方法實(shí)現(xiàn)了三份虛擬內(nèi)存指向同一份物理內(nèi)存。而 Linux 統(tǒng)計(jì)進(jìn)程 RSS 內(nèi)存占用的算法是比較脆弱的,這種多映射的方式并沒有考慮完整,因此根據(jù)當(dāng)前 Linux 采用大頁和小頁時(shí),其統(tǒng)計(jì)的開啟 ZGC 的 Java 進(jìn)程的內(nèi)存表現(xiàn)是不同的。在內(nèi)核使用小頁的 Linux 版本上,這種三映射的同一塊物理內(nèi)存會(huì)被 linux 的 RSS 占用算法統(tǒng)計(jì) 3 次,因此通常可以看到使用 ZGC 的 Java 進(jìn)程的 RSS 內(nèi)存膨脹了三倍左右,但是實(shí)際占用只有統(tǒng)計(jì)數(shù)據(jù)的三分之一,會(huì)對(duì)運(yùn)維或者其他業(yè)務(wù)造成一定的困擾。而在內(nèi)核使用大頁的 Linux 版本上,這部分三映射的物理內(nèi)存則會(huì)統(tǒng)計(jì)到 hugetlbfs inode 上,而不是當(dāng)前 Java 進(jìn)程上。

          2. 共享內(nèi)存調(diào)整

          ZGC 需要在 share memory 中建立一個(gè)內(nèi)存文件來作為實(shí)際物理內(nèi)存占用,因此當(dāng)要使用的 Java 的堆大小大于 /dev/shm 的大小時(shí),需要對(duì) /dev/shm 的大小進(jìn)行調(diào)整。通常來說,命令如下(下面是將 /dev/shm 調(diào)整為 64G):

          vi /etc/fstabtmpfs /dev/shm tmpfs defaults,size=65536M 0 0

          首先修改 fstab 中 shm 配置的大小,size 的值根據(jù)需求進(jìn)行修改,然后再進(jìn)行 shm 的 mount 和 umount。

          umount /dev/shmmount /dev/shm

          3. mmap 節(jié)點(diǎn)上限調(diào)整

          ZGC 的堆申請(qǐng)和傳統(tǒng)的 GC 有所不同,需要占用的 memory mapping 數(shù)目更多,即每個(gè) ZPage 需要 mmap 映射三次,這樣系統(tǒng)中僅 Java Heap 所占用的 mmap 個(gè)數(shù)為 (Xmx / zpage_size) * 3,默認(rèn)情況下 zpage_size 的大小為 2M。

          為了給 JNI 等 native 模塊中的 mmap 映射數(shù)目留出空間,內(nèi)存映射的數(shù)目應(yīng)該調(diào)整為 (Xmx / zpage_size)3*1.2。

          默認(rèn)的系統(tǒng) memory mapping 數(shù)目由文件 /proc/sys/vm/max_map_count 指定,通常數(shù)目為 65536,當(dāng)給 JVM 配置一個(gè)很大的堆時(shí),需要調(diào)整該文件的配置,使得其大于 (Xmx / zpage_size)3*1.2。


          43260fc43ccc205ea7de0393650edee6.webp

          ZGC在騰訊大規(guī)模的生產(chǎn)與實(shí)踐

          目前 Tencent Kona JDK11 的 ZGC 已經(jīng)在騰訊廣告大數(shù)據(jù)場(chǎng)景,騰訊云 VPC、WAF 等業(yè)務(wù)場(chǎng)景上長(zhǎng)期穩(wěn)定運(yùn)行,并協(xié)助業(yè)務(wù)取得了優(yōu)異的性能表現(xiàn)。

          1. 支持廣告海量數(shù)據(jù)查詢

          Hermes 是騰訊自研的大數(shù)據(jù)實(shí)時(shí)分析系統(tǒng),具有海量數(shù)據(jù)實(shí)時(shí)接入和存儲(chǔ)、低延遲查詢分析的特性,支持千級(jí)維度的多維分析,以及日增量萬億的海量日志接入和查詢分析。在廣告業(yè)務(wù)實(shí)時(shí) OLAP 分析業(yè)務(wù)中,要求 Hermes 系統(tǒng)上 99% 的 SQL 查詢端到端延遲不超過 3s,而采用默認(rèn)配置的 G1 GC 時(shí)僅 98.1% 的 SQL 查詢端到端延遲不超過 3s。通過切換 Kona 11 ZGC,SQL 端到端延遲滿足率上升為 99.5%,同時(shí)單個(gè) SQL 查詢中 GC 造成的延遲不超過 20ms。

          2. 超大堆支持

          騰訊 VPC 團(tuán)隊(duì)為騰訊云提供網(wǎng)絡(luò)控制服務(wù),該服務(wù)主要存儲(chǔ)用于云上資源通信的網(wǎng)絡(luò)配置等信息,并提供配置信息的查詢、修改和下發(fā)等服務(wù)。業(yè)務(wù)要求在 512G 內(nèi)存的機(jī)器上盡可能多的存儲(chǔ)配置信息(最大支持 800M 的監(jiān)聽數(shù)目),并且保證壓力場(chǎng)景下讀寫延遲不超過 1s。采用 G1GC 則會(huì)高頻率出現(xiàn)大量的延遲超過 10s,通過騰訊大數(shù)據(jù) JVM 團(tuán)隊(duì)配合切換 ZGC,并解決 ZGC 在業(yè)務(wù)遇到的 Mark Stack Overflow、進(jìn)入 Safepoint 緩慢、ZGC Mark 假死等問題,最終使得業(yè)務(wù)能夠在壓力場(chǎng)景下,將預(yù)期的業(yè)務(wù)存儲(chǔ)容量提升 12.5%,同時(shí)讀寫延遲不超過 50ms。

          3.  助力提升 SLA

          騰訊 WAF 團(tuán)隊(duì)采用 Java 來快速實(shí)現(xiàn)產(chǎn)品功能迭代及上線,其中,旁路安全服務(wù)是一個(gè)基于 Netty 框架的 Http 服務(wù),此服務(wù)對(duì)時(shí)延要求很嚴(yán)格,需要達(dá)到 99.99% 端到端請(qǐng)求時(shí)延小于 80ms 的 SLA 目標(biāo)。因此 GC 的 STW 對(duì)此服務(wù)有一定負(fù)面影響,需要進(jìn)一步降低“世界暫停”時(shí)間。在使用 ZGC 之前,WAF 團(tuán)隊(duì)使用的是 G1GC,前期花費(fèi)了大量時(shí)間對(duì) G1 GC 進(jìn)行選項(xiàng)調(diào)試,并進(jìn)行了代碼層面的修改。但由于 G1GC 本身的不足,仍然存在請(qǐng)求抖動(dòng)延遲,無法達(dá)到既定的 SLA 目標(biāo)。在騰訊大數(shù)據(jù) JVM 團(tuán)隊(duì)的配合下,切換 ZGC 之后,該業(yè)務(wù)的 P9999 請(qǐng)求延遲穩(wěn)定小于 80ms,為用戶提供了更快速、穩(wěn)定的服務(wù)。


          43260fc43ccc205ea7de0393650edee6.webp

          社區(qū)回饋

          騰訊大數(shù)據(jù) JVM 團(tuán)隊(duì)在支持業(yè)務(wù)切換 ZGC 的同時(shí),將遇到的相關(guān)問題和修復(fù)積極向社區(qū)報(bào)告和回饋,爭(zhēng)做 OpenJDK 社區(qū)好公民。

          1. ZGC 與 VectorAPI 聯(lián)合使用問題

          在廣告某業(yè)務(wù)中,上線 VectorAPI 以提升機(jī)器學(xué)習(xí)效率,同時(shí)打開 ZGC 以滿足服務(wù) SLA,在業(yè)務(wù)運(yùn)行過程中出現(xiàn)結(jié)果非預(yù)期現(xiàn)象,并且社區(qū)存在類似的錯(cuò)誤報(bào)告。通過對(duì) JIT 生產(chǎn)的匯編代碼進(jìn)行分析,發(fā)現(xiàn)存在 load barrier 缺失現(xiàn)象。經(jīng)分析,在 C2 的 Vector 優(yōu)化階段需要對(duì) Vector 節(jié)點(diǎn)進(jìn)行 Unbox 操作,該優(yōu)化階段會(huì)新生成一個(gè) load 節(jié)點(diǎn),并且又未考慮 GC Barrier 對(duì) load 操作的影響,即 ZGC 需要對(duì) load 操作生成 load barrier,從而導(dǎo)致這個(gè)新生成的 load 節(jié)點(diǎn)缺少 load barrier 信息,最終未能生成相關(guān) barrier 指令。通過對(duì) Vector 優(yōu)化階段新生產(chǎn)的 load 操作增加 GC barrier 處理流程,使得該階段能夠生成帶 gc barrier 信息的 load 節(jié)點(diǎn),從而在不同 GC 選項(xiàng)下均能生成對(duì)應(yīng)正確的 barrier 代碼。該修復(fù)貢獻(xiàn)給社區(qū)后以 P2 優(yōu)先級(jí)合入 JDK16。

          22ed9b654575b209880b0d93222c80a0.webp

          2. ZGC Mark Stack Overflow 問題

          騰訊云某業(yè)務(wù)對(duì) ZGC 進(jìn)行灰度時(shí),出現(xiàn) JVM 進(jìn)程崩潰現(xiàn)象,相關(guān)日志顯示是由于 ZGC 標(biāo)記階段使用的 Mark Stack 超過預(yù)先設(shè)定的 8G 內(nèi)存導(dǎo)致,而通常情況下 Mark Stack 的使用不會(huì)超過 32M。經(jīng)過業(yè)務(wù)全力配合,拿到一個(gè)可復(fù)現(xiàn)的場(chǎng)景進(jìn)行深入分析,發(fā)現(xiàn)在這種場(chǎng)景下 ZGC 對(duì)于 Mark Stack 的使用存在兩個(gè)缺陷:第一,大量的 Mark Stack 未使用滿就塞入全局隊(duì)列,造成單個(gè) Stack 內(nèi)存碎片問題;第二,大量的對(duì)象被多次壓入 Mark Stack 中,造成 Stack 中的 Entry 重復(fù)率很高,浪費(fèi) Stack 空間。騰訊大數(shù)據(jù) JVM 團(tuán)隊(duì)作出快速修復(fù),驗(yàn)證 Mark Stack Overflow 問題可解后,將該問題和修復(fù)報(bào)告 OpenJDK 社區(qū),社區(qū)基于提交的 patch 給出了更為優(yōu)雅的修復(fù)方案,并將騰訊大數(shù)據(jù) JVM 團(tuán)隊(duì)作為 co-author 聯(lián)合提交代碼入庫,目前兩個(gè)問題的修復(fù)均已入庫 JDK17。

          bc5fdafbfab70e77ff802c09b4b7726a.webp


          3.ZGC Mark 假死問題

          在分析 ZGC 在業(yè)務(wù)上的表現(xiàn)時(shí),需要打開 gc debug log 選項(xiàng),在啟動(dòng)后不久,出現(xiàn)進(jìn)程卡死現(xiàn)象。分析發(fā)現(xiàn)絕大部分 gc worker 線程處在“Concurrent Mark Try Terminate”階段,并且在等待“Concurrent Mark Idle”階段的 log 文件讀寫鎖,另外一個(gè) gc worker 線程處于寫 log 過程中,由此可以分析出由于 gc worker 線程均在搶 log 文件鎖,導(dǎo)致 gc worker 線程最終形成一種動(dòng)態(tài)死鎖狀態(tài),即所有的 gc worker 線程均處于“等鎖 ->拿鎖 ->釋放鎖”這種無限循環(huán)中。這種假死現(xiàn)象是由于 ZGC 的 Concurrent Mark 退出機(jī)制導(dǎo)致的,在退出機(jī)制中所有的 gc worker 線程會(huì)等待 1ms 來進(jìn)行狀態(tài)同步,而等待結(jié)束后會(huì)進(jìn)行相關(guān) log 打印,這個(gè)打印需要前述 log 文件鎖,從而導(dǎo)致動(dòng)態(tài)假死現(xiàn)象出現(xiàn)。騰訊大數(shù)據(jù) JVM 團(tuán)隊(duì)快速修復(fù)該問題,并提交社區(qū),目前該貢獻(xiàn)已合入 JDK17 中。

          6cca8908c08956e2acb16dc5e78257f9.webp

          43260fc43ccc205ea7de0393650edee6.webp

          Tencent Kona JDK 開源

          騰訊大數(shù)據(jù) JVM 團(tuán)隊(duì)的 Tencent Kona JDK 最新版本已經(jīng)正式對(duì)外發(fā)布,大家可以使用 Tencent Kona JDK 11.0.10-GA 享受到 ZGC 帶來的好處。

          Tencent Kona JDK 8.0.5-GA 同步更新 OpenJDK 8u282ga

          https://github.com/Tencent/TencentKona-8

          Tencent Kona JDK 11.0.10-GA 同步更新 OpenJDK 11.0.10-ga

          https://github.com/Tencent/TencentKona-11


          3db6640db318b3d18a1feef98f695141.webp

          免費(fèi)體驗(yàn)館

          消息隊(duì)列CKafka

          分布式、高吞吐量、高可擴(kuò)展性的消息服務(wù),具備數(shù)據(jù)壓縮、同時(shí)支持離線和實(shí)時(shí)數(shù)據(jù)處理等優(yōu)點(diǎn)。

          掃碼可免費(fèi)體驗(yàn)

          免費(fèi)體驗(yàn)路徑:云產(chǎn)品體驗(yàn)->基礎(chǔ)->消息隊(duì)列CKafka


          消息隊(duì)列TDMQ

          一款基于 Apache 頂級(jí)開源項(xiàng)目 Pulsar 自研的金融級(jí)分布式消息中間件。其計(jì)算與存儲(chǔ)分離的架構(gòu)設(shè)計(jì),使得它具備極好的云原生和 Serverless 特性,用戶按量使用,無需關(guān)心底層資源。

          掃碼點(diǎn)擊“立即使用”,即可免費(fèi)體驗(yàn)


          微服務(wù)平臺(tái)TSF

          穩(wěn)定、高性能的技術(shù)中臺(tái)。一個(gè)圍繞著應(yīng)用和微服務(wù)的 PaaS 平臺(tái),提供應(yīng)用全生命周期管理、數(shù)據(jù)化運(yùn)營、立體化監(jiān)控和服務(wù)治理等功能。TSF 擁抱 Spring Cloud 、Service Mesh 微服務(wù)框架,幫助企業(yè)客戶解決傳統(tǒng)集中式架構(gòu)轉(zhuǎn)型的困難,打造大規(guī)模高可用的分布式系統(tǒng)架構(gòu),實(shí)現(xiàn)業(yè)務(wù)、產(chǎn)品的快速落地。

          掃碼點(diǎn)擊“免費(fèi)體驗(yàn)”,即可免費(fèi)體驗(yàn)


          微服務(wù)引擎TSE

          高效、穩(wěn)定的注冊(cè)中心托管,助力您快速實(shí)現(xiàn)微服務(wù)架構(gòu)轉(zhuǎn)型。

          掃碼點(diǎn)擊“立即申請(qǐng)”,即可免費(fèi)體驗(yàn)


          彈性微服務(wù)TEM

          面向微服務(wù)應(yīng)用的 Serverless PaaS 平臺(tái),實(shí)現(xiàn)資源 Serverless 化與微服務(wù)架構(gòu)的完美結(jié)合,提供一整套開箱即用的微服務(wù)解決方案。彈性微服務(wù)幫助用戶創(chuàng)建和管理云資源,并提供秒級(jí)彈性伸縮,用戶可按需使用、按量付費(fèi),極大程度上幫用戶節(jié)約運(yùn)維和資源成本。讓用戶充分聚焦企業(yè)核心業(yè)務(wù)本身,助力業(yè)務(wù)成功。

          掃碼點(diǎn)擊“立即申請(qǐng),即可免費(fèi)體驗(yàn)




          往期

          推薦


          《騰訊云中間件產(chǎn)品月報(bào) | 2021年第5期》

          《當(dāng)我們?cè)诹母呖捎脮r(shí),我們其實(shí)在聊什么?》

          《深度解析:Pulsar的消息存儲(chǔ)機(jī)制和Bookie的GC機(jī)制原理》

          《消息系統(tǒng)興起二次革命:Kafka不需要ZooKeeper》




          13e0287c8deb5f7b0f98f6654627e7be.webp


          掃描下方二維碼關(guān)注本公眾號(hào),

          了解更多微服務(wù)、消息隊(duì)列的相關(guān)信息!

          解鎖超多鵝廠周邊!


          3ee885108353de721137e52f57b3fbb2.webp戳原文,了解更多Kona的信息
          32ff5f3fcc11021c1b5fd91100253ca1.webp點(diǎn)亮在看,你最好看


          瀏覽 68
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色视频免费无码在线播放观看 | 美女91影院 | 国产精品久久久久毛片SUV | 高清无码做爱 | 日日爽夜夜 |