京東面試:什么是gc尖刺? 怎么解決由于 gc 導(dǎo)致的尖刺?
尼恩說(shuō)在前面
在40歲老架構(gòu)師 尼恩的讀者交流群(50+)中,最近有小伙伴拿到了一線互聯(lián)網(wǎng)企業(yè)如得物、阿里、滴滴、極兔、有贊、希音、百度、網(wǎng)易、美團(tuán)的面試資格,遇到很多很重要的面試題:
- 什么是gc尖刺? 怎么 解決由于 gc 導(dǎo)致的 尖刺?
 - GC 毛刺見(jiàn)過(guò)嗎, 如何排查?
 
最近有小伙伴在面試京東、 阿里、希音等大廠,又遇到了相關(guān)的面試題。
小伙伴 沒(méi)系統(tǒng)梳理, 支支吾吾的說(shuō)了幾句,面試官不滿意, 掛了。
特別說(shuō)明:
GC 毛刺 很多的場(chǎng)景。
下面的大廠 案例, 介紹的是 一個(gè)場(chǎng)景,這個(gè)場(chǎng)景是: 大規(guī)模的長(zhǎng)命小對(duì)象 , 在 新生代 ygc 頻繁復(fù)制,導(dǎo)致的GC 毛刺。
特別說(shuō)明:
其他的GC 毛刺 場(chǎng)景 后面尼恩再找一些案例來(lái)進(jìn)行展示。
本文的案例 ,以及其他的gc 毛刺案例, 都來(lái)自互聯(lián)網(wǎng),不是尼恩原創(chuàng) 案例。
如果原作者不愿意 尼恩用來(lái)作為 學(xué)習(xí)材料 放在公眾號(hào), 可以找尼恩反饋,尼恩立即從本公眾號(hào)撤下來(lái)。
一、什么是gc尖刺?
GC尖刺(Garbage Collection Spike) ,有時(shí)也被稱為GC毛刺或GC突刺 , 并不是某個(gè)官方術(shù)語(yǔ),而是線上運(yùn)維的“體感”說(shuō)法。
大概意思是: 在一條本來(lái)平穩(wěn)的 RT(響應(yīng)時(shí)間)或 CPU 曲線上,突然豎起一根像刺一樣的尖峰,持續(xù)時(shí)間從幾十毫秒到幾秒不等,看上去很多 突刺。
gc尖刺 根因 是: 垃圾回收器在某一刻發(fā)生了長(zhǎng)時(shí)間停頓(Stop-The-World,簡(jiǎn)稱 STW)。
由于Stop-The-World(STW) 暫停,導(dǎo)致應(yīng)用程序 RT(響應(yīng)時(shí)間)或 CPU 曲線上 出現(xiàn)的突然而顯著的峰值。
簡(jiǎn)單來(lái)說(shuō),就是GC過(guò)程中,JVM會(huì)暫停所有應(yīng)用線程來(lái)執(zhí)行垃圾回收,如果這次暫停時(shí)間過(guò)長(zhǎng),就會(huì)像路上的突然堵車一樣,導(dǎo)致系統(tǒng)性能出現(xiàn)瞬間的“卡頓”。
GC尖刺的背后,往往是內(nèi)存管理不當(dāng)或垃圾回收器配置不佳。
一些典型GC尖刺誘因:
1、內(nèi)存分配問(wèn)題
- 短命大對(duì)象:在循環(huán)或高頻方法中持續(xù)創(chuàng)建大對(duì)象(如大的數(shù)組、集合),這些對(duì)象可能迅速占滿新生代,導(dǎo)致Minor GC頻繁,且每次回收耗時(shí)增加。更糟的是,如果大對(duì)象過(guò)早晉升到老年代,還會(huì)引發(fā)不必要的Full GC,導(dǎo)致gc 尖刺。
 - 內(nèi)存泄漏:由于代碼缺陷(如未清理的靜態(tài)集合、未關(guān)閉的資源、
ThreadLocal使用不當(dāng)),導(dǎo)致對(duì)象無(wú)法被回收。老年代內(nèi)存被無(wú)效對(duì)象逐漸填滿,最終觸發(fā)長(zhǎng)時(shí)間停頓的Full GC,但回收效果甚微,內(nèi)存使用率居高不下,導(dǎo)致gc 尖刺。 - 大規(guī)模的長(zhǎng)命小對(duì)象在年輕代復(fù)制: 本文的例子中,出現(xiàn)了 大規(guī)模長(zhǎng)命小對(duì)象(約 500MB),在年輕代的 eden和 幸存者區(qū)來(lái)回復(fù)制,導(dǎo)致gc 尖刺。
 
2、垃圾回收器配置與選擇
- 堆內(nèi)存設(shè)置不合理:堆內(nèi)存過(guò)小會(huì)導(dǎo)致GC頻繁發(fā)生;堆內(nèi)存過(guò)大則會(huì)使單次GC需要處理的數(shù)據(jù)量增多,可能導(dǎo)致STW時(shí)間變長(zhǎng)。
 - GC參數(shù)不匹配:例如,G1垃圾回收器的 
MaxGCPauseMillis(預(yù)期最大停頓時(shí)間)設(shè)置過(guò)小,可能會(huì)迫使GC更頻繁地工作以試圖達(dá)到目標(biāo),反而影響整體吞吐量并可能引發(fā)問(wèn)題。 - GC器選擇不當(dāng):像ZGC和ShenandoahGC這類低停頓回收器,雖然STW時(shí)間極短,但在高吞吐量計(jì)算密集型場(chǎng)景下,其并發(fā)執(zhí)行會(huì)與業(yè)務(wù)線程競(jìng)爭(zhēng)CPU資源,可能導(dǎo)致整體響應(yīng)時(shí)間上升和周期性尖刺。
 
3、其他問(wèn)題:如系統(tǒng)資源與外部因素
- 日志打印過(guò)量:大量同步日志寫入會(huì)爭(zhēng)搶磁盤I/O鎖,導(dǎo)致線程阻塞。同時(shí),日志文件快速增大觸發(fā)的滾動(dòng)清理操作也會(huì)消耗大量CPU和I/O資源,間接引發(fā)或加劇GC壓力。
 - 定時(shí)任務(wù)處理大數(shù)據(jù)集:定時(shí)任務(wù)一次性加載和處理大量數(shù)據(jù)(如從數(shù)據(jù)庫(kù)撈出數(shù)十萬(wàn)條記錄),會(huì)在短時(shí)間內(nèi)產(chǎn)生海量對(duì)象,給GC帶來(lái)巨大壓力。
 
GC尖刺的危害:
GC尖刺的危害是直接且嚴(yán)重的,尤其在高并發(fā)、低延遲要求的系統(tǒng)中:
- 接口響應(yīng)時(shí)間劇烈抖動(dòng):最直接的表現(xiàn)就是應(yīng)用服務(wù)的P99、P999延遲(如99%或99.9%請(qǐng)求的響應(yīng)時(shí)間)出現(xiàn)周期性或突發(fā)性的尖峰,導(dǎo)致用戶體驗(yàn)下降。
 - 系統(tǒng)吞吐量下降:頻繁且長(zhǎng)時(shí)間的GC會(huì)占用大量系統(tǒng)資源(CPU資源被大量用于垃圾回收而非業(yè)務(wù)處理),導(dǎo)致系統(tǒng)整體處理能力(QPS/TPS)降低。
 - 上游調(diào)用超時(shí)與故障擴(kuò)散:若GC導(dǎo)致服務(wù)響應(yīng)超時(shí),可能引起上游調(diào)用方(如網(wǎng)關(guān)、其他微服務(wù))連鎖超時(shí)失敗,在分布式系統(tǒng)中可能引發(fā)雪崩效應(yīng)。
 
二、問(wèn)題復(fù)盤
在高并發(fā)、低延遲的服務(wù)中,GC 的行為會(huì)直接影響服務(wù)的響應(yīng)時(shí)間和穩(wěn)定性。
本文場(chǎng)景 討論的場(chǎng)景, 是 源于一個(gè)真實(shí)的大廠 高并發(fā)系統(tǒng)(系統(tǒng)A),該系統(tǒng)的 QPS 日常在十萬(wàn)級(jí)別,大促期間甚至?xí)^(guò) 40W,且對(duì)響應(yīng)時(shí)間有毫秒級(jí)的嚴(yán)格要求。
任何由于GC 垃圾回收引起的停頓, 都可能導(dǎo)致超時(shí)和業(yè)務(wù)成功率下降。
image-20251011101749451
在大促期間(QPS 40W),的巡檢監(jiān)控中,發(fā)現(xiàn)上游調(diào)用方出現(xiàn)零星超時(shí)告警。
通過(guò)監(jiān)控系統(tǒng)定位到系統(tǒng)A在特定時(shí)段出現(xiàn)了周期性響應(yīng)時(shí)間毛刺(如下圖所示),這些毛刺與GC日志中的Full GC時(shí)間點(diǎn)高度吻合,初步判斷是GC停頓引發(fā)了服務(wù)抖動(dòng)。
圖片
問(wèn)題根因:大規(guī)模 長(zhǎng)生命小對(duì)象引發(fā)新生代復(fù)制風(fēng)暴。
先說(shuō)結(jié)論:我們發(fā)現(xiàn)系統(tǒng)A中緩存了一批業(yè)務(wù)索引數(shù)據(jù),這些數(shù)據(jù)具有以下特點(diǎn)。
- 大規(guī)模 小對(duì)象: 體積?。總€(gè)對(duì)象幾KB至幾十KB)、總量大(約500MB)
 - 長(zhǎng)生命對(duì)象 :一旦加載,長(zhǎng)時(shí)間存活(通常貫穿整個(gè)服務(wù)生命周期)
 - 在業(yè)務(wù)邏輯中頻繁被使用
 
默認(rèn)情況下,這些對(duì)象會(huì)在新生代的 Eden 區(qū)創(chuàng)建,由于存活時(shí)間長(zhǎng),它們會(huì)在 Survivor 區(qū)來(lái)回復(fù)制,直至年齡達(dá)到閾值后才被晉升到老年代。
在這個(gè)過(guò)程中,會(huì)產(chǎn)生兩方面開(kāi)銷:
(1) 復(fù)制開(kāi)銷:大規(guī)模對(duì)象在 Survivor 區(qū)之間來(lái)回復(fù)制,CPU 消耗顯著;
(2) 晉升開(kāi)銷:對(duì)象年齡達(dá)到閾值時(shí),批量復(fù)制到老年代,容易引發(fā)停頓。
尤其是在 Survivor 區(qū)空間不足或?qū)ο髲?fù)制頻率較高時(shí),Young GC 耗時(shí)明顯增加,嚴(yán)重時(shí)甚至?xí)|發(fā)提前晉升或直接進(jìn)入老年代,引發(fā)了GC尖刺問(wèn)題。
解決這類問(wèn)題,大致分為以下三步處理:
- 排查問(wèn)題:定位根因
 - 分析問(wèn)題:找到解決方案
 - 優(yōu)化過(guò)程:解決問(wèn)題
 
優(yōu)化的思路: 盡早晉升,也就是 讓 大規(guī)模的長(zhǎng)命小對(duì)象(業(yè)務(wù)索引數(shù)據(jù))盡早晉升到老年代, 或者 讓索引直接分配到老年代,從而加速 加速索引復(fù)制。 當(dāng)然, 也會(huì)考慮 升級(jí) GC , 升級(jí)通過(guò) 斷流發(fā)布 +主動(dòng)預(yù)熱 規(guī)避GC。 接下來(lái)和大家一一介紹。
在不加一臺(tái)機(jī)器、不改變流量大小的前提下,系統(tǒng)成功率(抖動(dòng)時(shí))逐步優(yōu)化效果為:95% => 98% => 99.5% => 99.995%,保障系統(tǒng)高可用。
下面將詳細(xì)介紹整個(gè)排查和優(yōu)化過(guò)程。
三、排查過(guò)程
1、初步常規(guī)分析
首先從上游業(yè)務(wù)報(bào)警入手,發(fā)現(xiàn)報(bào)錯(cuò)均為同步調(diào)用超時(shí)(TimeoutException),因此聚焦系統(tǒng)A自身狀態(tài),開(kāi)展第一輪排查:
- 對(duì)比故障時(shí)間點(diǎn)前后流量監(jiān)控,未見(jiàn)明顯峰值,CPU使用率和系統(tǒng)負(fù)載均處于正常水位,可排除流量激增導(dǎo)致過(guò)載的可能。
 - 系統(tǒng)A為純內(nèi)存計(jì)算型服務(wù),無(wú)數(shù)據(jù)庫(kù)、緩存或RPC調(diào)用,不存在外部組件拖慢整體響應(yīng)的因素。
 - 系統(tǒng)雖高并發(fā),但請(qǐng)求間無(wú)同步互斥邏輯,不存在分布式鎖或線程鎖競(jìng)爭(zhēng)導(dǎo)致的阻塞超時(shí)。
 
經(jīng)過(guò)首輪排查,已排除流量激增、外部服務(wù)有瓶頸、并發(fā)鎖等可能影響因素,但并未定位到根因,需進(jìn)一步向內(nèi)挖掘系統(tǒng)自身狀態(tài)。
2、定位根因
在排除常規(guī)疑點(diǎn)后,我們開(kāi)始查看系統(tǒng)內(nèi)部日志與監(jiān)控,發(fā)現(xiàn)關(guān)鍵日志證據(jù):發(fā)現(xiàn)系統(tǒng)在抖動(dòng)發(fā)生前執(zhí)行了一次索引發(fā)布。(熱更新),如下圖所示:
圖片
說(shuō)明:該系統(tǒng)每隔15分鐘會(huì)全量替換內(nèi)存中的業(yè)務(wù)索引(一個(gè)約500MB的復(fù)雜Map結(jié)構(gòu)),此過(guò)程瞬間產(chǎn)生大量新對(duì)象。
檢查對(duì)應(yīng)時(shí)間點(diǎn)的GC日志,發(fā)現(xiàn)Young GC耗時(shí)異常,其中Object Copy階段耗時(shí)超過(guò)200ms,如下圖:
圖片
Object Copy是YGC的關(guān)鍵階段:存活對(duì)象會(huì)從Eden區(qū)復(fù)制到Survivor區(qū)(或晉升老年代)。大量存活對(duì)象導(dǎo)致復(fù)制開(kāi)銷陡增,引發(fā)GC尖刺。
定位根因:系統(tǒng)在索引發(fā)布后,新生成的索引對(duì)象在Young GC中反復(fù)復(fù)制,且由于對(duì)象數(shù)量大、存活時(shí)間長(zhǎng),導(dǎo)致Copy階段STW過(guò)長(zhǎng),業(yè)務(wù)線程暫停,上游超時(shí)增多。
也就是:大規(guī)模長(zhǎng)生命對(duì)象在新生代頻繁復(fù)制,引起GC停頓放大,最終導(dǎo)致服務(wù)超時(shí)。
四、問(wèn)題的定位與分析
1、常規(guī)優(yōu)化思路分析
面對(duì)GC暫停時(shí)間過(guò)長(zhǎng)的問(wèn)題,通常有以下幾種優(yōu)化思路:
image-20251011164004333
然而,在本次場(chǎng)景中,上述常規(guī)方案大多難以直接應(yīng)用或效果有限。
原因如下:
- 首先,經(jīng)過(guò)細(xì)致排查,代碼層面并未發(fā)現(xiàn)明顯缺陷,索引結(jié)構(gòu)也已高度壓縮,沒(méi)有進(jìn)一步優(yōu)化的空間。同時(shí),受限于業(yè)務(wù)特性,索引更新機(jī)制必須采用全量替換,無(wú)法實(shí)現(xiàn)增量更新。
 - 其次,單純?cè)黾訖C(jī)器數(shù)量雖然可以通過(guò)分流請(qǐng)求來(lái)減少單機(jī)在STW期間影響的請(qǐng)求量,但這本質(zhì)上是一種規(guī)避而非解決,不僅無(wú)法從根本上消除GC停頓,還會(huì)造成資源利用率下降和成本上升。
 - 此外,堆外內(nèi)存方案雖然能規(guī)避GC管理,但需要頻繁的序列化和反序列化操作。在高并發(fā)訪問(wèn)的場(chǎng)景下,這部分額外開(kāi)銷對(duì)延遲的影響無(wú)法忽視,與系統(tǒng)所需的毫秒級(jí)響應(yīng)目標(biāo)相悖。
 
因此,綜合評(píng)估后,我們決定將優(yōu)化重點(diǎn)放在JVM參數(shù)調(diào)優(yōu)上:通過(guò)精細(xì)調(diào)整垃圾回收器的行為模式,優(yōu)化內(nèi)存分配和晉升策略,盡可能降低大規(guī)模對(duì)象復(fù)制帶來(lái)的負(fù)面影響,從而在現(xiàn)有架構(gòu)下保障服務(wù)的高可用性。
2、GC日志深度解析與根因推演
基于前期分析,問(wèn)題的核心在于YGC的Object Copy階段:大規(guī)模索引對(duì)象的復(fù)制操作耗時(shí)過(guò)長(zhǎng),導(dǎo)致STW時(shí)間增加,進(jìn)而引發(fā)上游請(qǐng)求超時(shí)。本節(jié)將通過(guò)詳細(xì)分析GC日志,還原完整的GC行為模式。
當(dāng)前JVM核心參數(shù)配置如下:
-Xms12g -Xmx12g                  # 堆內(nèi)存固定為12GB
-XX:MetaspaceSize=512m           # 元空間初始大小512MB
-XX:MaxMetaspaceSize=512m        # 元空間最大限制512MB  
-XX:+UseG1GC                     # 使用G1垃圾回收器
-XX:G1HeapReginotallow=16M         # 設(shè)置Region大小為16MB
-XX:MaxGCPauseMillis=100         # 目標(biāo)最大暫停時(shí)間100ms
-XX:InitiatingHeapOccupancyPercent=45 # 老年代占用45%時(shí)啟動(dòng)混合GC
-XX:+HeapDumpOnOutOfMemoryError  # OOM時(shí)生成堆轉(zhuǎn)儲(chǔ)
-XX:MaxDirectMemorySize=1g       # 最大直接內(nèi)存限制1GB通過(guò)內(nèi)部監(jiān)控平臺(tái)ATP對(duì)GC日志進(jìn)行可視化分析,下圖中標(biāo)出了各 GC 事件的時(shí)間點(diǎn)和變化曲線:
圖片
圖中清晰展示了以下關(guān)鍵信息:
① 藍(lán)色圓點(diǎn)(YGC事件):每個(gè)圓點(diǎn)代表一次Young GC事件。圖中可見(jiàn)大量密集分布的藍(lán)點(diǎn),表明YGC發(fā)生頻率高且耗時(shí)極短(毫秒級(jí)),能夠迅速完成年輕代垃圾回收——這是高吞吐、低延遲系統(tǒng)的理想表現(xiàn),符合預(yù)期。
② 粉色折線(堆內(nèi)存占用):該折線反映堆內(nèi)存使用量的動(dòng)態(tài)變化,呈現(xiàn)規(guī)律的鋸齒形態(tài)——快速上升后驟降。這種模式符合預(yù)期:由于系統(tǒng)流量大,請(qǐng)求處理過(guò)程中會(huì)持續(xù)產(chǎn)生大量短期存活的臨時(shí)對(duì)象,使內(nèi)存占用快速上升;當(dāng)Eden區(qū)空間不足時(shí)觸發(fā)YGC,迅速回收這些對(duì)象,使內(nèi)存占用回落至低點(diǎn)。
③ 異常藍(lán)點(diǎn)(長(zhǎng)耗時(shí)YGC):部分藍(lán)點(diǎn)明顯遠(yuǎn)離橫軸,表示這些YGC的耗時(shí)顯著高于正常水平。這些點(diǎn)與之前在日志中手動(dòng)識(shí)別出的長(zhǎng)耗時(shí)記錄相符,是導(dǎo)致服務(wù)抖動(dòng)的直接原因,需要重點(diǎn)關(guān)注。
④ 紫色折線(老年代占用):該曲線反映老年代內(nèi)存使用情況。正常情況下,因絕大多數(shù)對(duì)象在年輕代就被回收,老年代占用率增長(zhǎng)緩慢。但值得關(guān)注的是,每次長(zhǎng)耗時(shí)YGC出現(xiàn)時(shí),紫色折線都呈現(xiàn)明顯的階梯式躍升,表明此時(shí)有大量對(duì)象晉升至老年代,這一現(xiàn)象需要重點(diǎn)關(guān)注。
進(jìn)一步觀察發(fā)現(xiàn),長(zhǎng)耗時(shí)YGC總是成對(duì)出現(xiàn),且具有“第一次晉升量少、第二次晉升量多”的規(guī)律,如下圖所示:
圖片
綜合所有線索,可以完整推演出問(wèn)題發(fā)生的過(guò)程:
階段一:系統(tǒng)創(chuàng)建新索引對(duì)象(約500MB),這些對(duì)象被分配在Eden區(qū)
階段二:Eden區(qū)空間不足觸發(fā)第一次YGC,新索引作為存活對(duì)象被復(fù)制到Survivor區(qū),大量對(duì)象復(fù)制導(dǎo)致STW長(zhǎng)達(dá)200ms+
階段三:業(yè)務(wù)代碼完成索引切換,將GcRoot指向新索引,同時(shí)斷開(kāi)舊索引引用
階段四:再次觸發(fā)YGC,新索引從Survivor區(qū)晉升到老年代,舊索引被回收,再次產(chǎn)生200ms+ STW
階段五:新索引穩(wěn)定存在于老年代,后續(xù)YGC只需處理小對(duì)象,恢復(fù)毫秒級(jí)響應(yīng)
通過(guò)這一分析,我們準(zhǔn)確定位了GC尖刺的根本原因:大規(guī)模長(zhǎng)生命周期對(duì)象在年輕代經(jīng)歷了兩次完整的復(fù)制過(guò)程(Survivor區(qū)復(fù)制和老年代晉升),導(dǎo)致雙倍的STW停頓時(shí)間。
image-20250929131905130
五、優(yōu)化過(guò)程
在明確了問(wèn)題根源——每次新建的大索引對(duì)象在年輕代中經(jīng)歷多次復(fù)制,引發(fā)長(zhǎng)時(shí)間 Young GC 停頓——之后,
我們圍繞減少?gòu)?fù)制次數(shù)、降低暫停時(shí)間的目標(biāo),設(shè)計(jì)了如下優(yōu)化方案。
image-20251011171830395
1.策略一:讓索引盡早晉升至老年代
默認(rèn)情況下,新創(chuàng)建的對(duì)象在經(jīng)歷一定次數(shù)的 Young GC 后才會(huì)晉升到老年代。我們的核心思路是改寫這個(gè)流程,讓大索引以最快路徑進(jìn)入老年代,避免在年輕代中反復(fù)復(fù)制。
1.1 調(diào)整晉升閾值:MaxTenuringThreshold
MaxTenuringThreshold參數(shù)用于設(shè)定對(duì)象在晉升至老年代前,能在年輕代中經(jīng)歷的最大 GC 次數(shù)。默認(rèn)值通常為 15,意味著對(duì)象需要在 Survivor 區(qū)之間來(lái)回拷貝多次,才有可能晉升。
通過(guò)分析線上 GC 日志,我們發(fā)現(xiàn) G1GC 對(duì)大型索引對(duì)象進(jìn)行了優(yōu)化(Direct Tenuring)。該索引的實(shí)際流轉(zhuǎn)路徑為:Eden → S0 → Old,僅經(jīng)歷了 2 次復(fù)制,而非默認(rèn)的 15 次。這相當(dāng)于 JVM 自動(dòng)將 MaxTenuringThreshold動(dòng)態(tài)調(diào)整為了 1,其過(guò)程如下:
階段一:新索引在 Eden 區(qū)創(chuàng)建,年齡(Age)為 0。
階段二:發(fā)生第一次 Young GC,索引存活。由于當(dāng)前年齡(0)小于閾值(1),索引從 Eden 被復(fù)制到 S0 區(qū),年齡增長(zhǎng)為 1。
階段三:發(fā)生第二次 Young GC,索引依然存活。此時(shí)年齡(1)等于閾值(1),索引被復(fù)制到 Old 區(qū),完成晉升。
我們嘗試手動(dòng)設(shè)置 -XX:MaxTenuringThreshold=1進(jìn)行驗(yàn)證,GC 日志證實(shí)索引的流轉(zhuǎn)路徑仍是 Eden → S0 → Old。

能不能進(jìn)一步的優(yōu)化?
很容易想到,能否將閾值設(shè)置為 0,讓索引在第一次 GC 時(shí)就直接從 Eden 晉升到 Old,完全跳過(guò) Survivor 區(qū)?
這樣,復(fù)制次數(shù)將從 2 次降為 1 次,預(yù)計(jì)暫停時(shí)間可減少近一半。
將參數(shù)修改為 -XX:MaxTenuringThreshold=0后,流程變?yōu)椋?/span>
階段一:新索引在 Eden 區(qū)創(chuàng)建,年齡為 0。
階段二:發(fā)生 Young GC,索引存活。由于當(dāng)前年齡(0)已等于閾值(0),索引被直接復(fù)制到 Old 區(qū)。
實(shí)驗(yàn)結(jié)果的 GC 日志顯示,Young GC 后年輕代的使用量驟降至接近零,證明大型索引已被直接晉升到老年代,優(yōu)化生效。
圖片
優(yōu)化效果總結(jié):
此優(yōu)化在不修改任何業(yè)務(wù)代碼、不增加硬件成本的前提下,通過(guò)調(diào)整一個(gè) JVM 參數(shù),便將因索引切換導(dǎo)致的長(zhǎng)暫停 GC 次數(shù)減半。
從系統(tǒng)監(jiān)控來(lái)看,索引切換期間的報(bào)錯(cuò)量顯著減少,服務(wù)成功率從 95% 提升至 98%。
image-20251011172819461
1.2 其他相關(guān)參數(shù)實(shí)驗(yàn)
在通過(guò) MaxTenuringThreshold成功優(yōu)化后,我們進(jìn)一步探索了其他能控制對(duì)象晉升策略的參數(shù),以尋求更優(yōu)解或替代方案。
1) InitialTenuringThreshold
此參數(shù)與 MaxTenuringThreshold作用類似,用于設(shè)定對(duì)象晉升的初始年齡閾值。
實(shí)測(cè)表明,設(shè)置 -XX:InitialTenuringThreshold=1同樣可以將索引的復(fù)制次數(shù)從 2 次降為 1 次,優(yōu)化效果與 MaxTenuringThreshold=0類似,都能有效提升系統(tǒng)穩(wěn)定性。
圖片
2) AlwaysTenure
這是一個(gè)更為極端的參數(shù),其字面含義是“總是晉升”。
開(kāi)啟后,所有在 Young GC 中存活的對(duì)象都會(huì)直接晉升到老年代,完全跳過(guò) Survivor 區(qū)。
實(shí)測(cè)設(shè)置 -XX:+AlwaysTenure后,同樣達(dá)到了減少一次復(fù)制的效果。其對(duì)象流轉(zhuǎn)路徑可概括為:
flowchart LR
    A[Eden] -- YGC / 存活 --> B[Old]
關(guān)于 AlwaysTenure 的說(shuō)明:
- 多次 GC 現(xiàn)象:因?yàn)樗饕龑?duì)象龐大,而 Eden 區(qū)剩余空間有限,其構(gòu)建過(guò)程可能橫跨多次 Young GC。每次 GC 都會(huì)將已構(gòu)建完成的部分索引直接晉升至老年代。因此,圖中顯示經(jīng)歷了 3 次 YGC 才將完整索引搬到老年代,這與“減少單次晉升的復(fù)制次數(shù)”的結(jié)論并不矛盾。它只是將原本需要在 2 次 GC 中完成的兩次復(fù)制,變成了在 3 次 GC 中完成的三次直接晉升(每次復(fù)制一次)。
 - 設(shè)計(jì)思想:
AlwaysTenure的設(shè)計(jì)理念是禁用 Survivor 區(qū),僅使用 Eden 和 Old 區(qū)。與之相反的參數(shù)是-XX:+NeverTenure,它會(huì)試圖讓對(duì)象永遠(yuǎn)留在年輕代,禁用 Old 區(qū)。兩者都是非常極端的策略,僅適用于特定的業(yè)務(wù)場(chǎng)景。 - 對(duì)老年代的影響:通常,降低晉升閾值會(huì)讓更多短期存活的對(duì)象進(jìn)入老年代,從而增加 Full GC 的風(fēng)險(xiǎn)。但我們的業(yè)務(wù)場(chǎng)景特殊性在于對(duì)象存活時(shí)間兩極分化:RPC 請(qǐng)求產(chǎn)生的臨時(shí)對(duì)象生命周期極短(毫秒級(jí)),而索引對(duì)象生命周期極長(zhǎng)(數(shù)十分鐘以上)。因此,在 Young GC 時(shí),臨時(shí)對(duì)象早已被回收,而索引對(duì)象是唯一需要被晉升的。修改這些參數(shù)并不會(huì)導(dǎo)致大量本應(yīng)被回收的短命對(duì)象進(jìn)入老年代,故不會(huì)增加 Full GC 的負(fù)擔(dān)。
 
2.嘗試讓索引直接分配至老年代
通過(guò)上面調(diào)整晉升策略,我們成功地將索引的復(fù)制次數(shù)從 2 次減少到 1 次。一個(gè)更極端的想法隨之產(chǎn)生:能否讓索引在創(chuàng)建時(shí)就直接分配在老年代,實(shí)現(xiàn) 0 次復(fù)制,從而從根本上避免由復(fù)制引起的停頓?
這個(gè)理想的分配路徑如下圖所示:
image-20251011173847017
圍繞此目標(biāo),我們進(jìn)行了以下兩種嘗試,但均未取得預(yù)期效果。
2.1 嘗試一:PretenureSizeThreshold
PretenureSizeThreshold是一個(gè)經(jīng)典的 JVM 參數(shù),旨在讓大于指定大小的對(duì)象直接在老年代分配,以避免在年輕代發(fā)生昂貴的復(fù)制操作。
遺憾的是,該參數(shù)在 G1 垃圾收集器下是不生效的。調(diào)整此參數(shù)后,通過(guò)監(jiān)控發(fā)現(xiàn)系統(tǒng)穩(wěn)定性指標(biāo)并無(wú)改善,GC 日志也顯示索引依然在年輕代中創(chuàng)建和流轉(zhuǎn)。
2.2 嘗試二:G1HeapRegionSize 與 Humongous Object
G1GC 有一個(gè)內(nèi)置機(jī)制用于處理大對(duì)象(Humongous Object)。它將堆劃分為多個(gè)大小相等的 Region(區(qū)域)。當(dāng)一個(gè)對(duì)象的大小超過(guò)單個(gè) Region 容量的一半時(shí),它就會(huì)被視為 Humongous Object,并被直接分配在老年代的特殊區(qū)域中。
理論上,通過(guò)調(diào)整 -XX:G1HeapRegionSize可以控制 Region 的大小,從而讓我們的索引滿足 Humongous Object 的條件。
我們?cè)龃罅?nbsp;G1HeapRegionSize以確保索引整體大小超過(guò)其一半,但優(yōu)化后系統(tǒng)在索引切換時(shí)依然出現(xiàn)抖動(dòng)。分析 GC 日志,索引的分配路徑依然是 Eden → Survivor → Old,并未被識(shí)別為 Humongous Object。
根本原因在于索引對(duì)象的物理結(jié)構(gòu)與邏輯結(jié)構(gòu)上
- 邏輯結(jié)構(gòu):從業(yè)務(wù)視角看,我們有一個(gè)約 500MB 的“大索引對(duì)象”。
 - 物理結(jié)構(gòu):但從 JVM 的內(nèi)存分配視角看,這個(gè)“大索引”實(shí)際上是由上百萬(wàn)個(gè)獨(dú)立的小對(duì)象(如 Map Entry、自定義數(shù)據(jù)結(jié)構(gòu)等)在程序運(yùn)行過(guò)程中逐個(gè)構(gòu)建而成的。JVM 每次通過(guò) 
new關(guān)鍵字分配的是這些小型個(gè)體對(duì)象,它們的大小遠(yuǎn)小于G1HeapRegionSize的一半,因此完全符合在 Eden 區(qū)分配的條件。 
除非是像 int[] arr = new int[1000000000];這樣,在代碼層面明確聲明分配的、單一的、巨大的連續(xù)數(shù)組,JVM 才能在一次分配中就識(shí)別出其大小并將其直接作為 Humongous Object 處理。
對(duì)于由海量小對(duì)象聚合而成的邏輯大對(duì)象,無(wú)法通過(guò)調(diào)整標(biāo)準(zhǔn) JVM 參數(shù)讓其直接在老年代分配。此優(yōu)化路徑在當(dāng)前業(yè)務(wù)代碼結(jié)構(gòu)下不可行。
3.策略三:加速索引的復(fù)制過(guò)程
在不改變復(fù)制次數(shù)的情況下,我們嘗試通過(guò)調(diào)整 GC 相關(guān)參數(shù)來(lái)提升復(fù)制速度。
參數(shù)名  | 作用  | 實(shí)測(cè)效果  | 
  | 設(shè)置 G1 的目標(biāo)最大停頓時(shí)間  | 效果不明顯。目標(biāo)停頓僅是期望,無(wú)法突破物理限制  | 
  | 設(shè)置 STW 階段并行 GC 的線程數(shù)  | 效果不明顯。默認(rèn)值已接近核心數(shù),優(yōu)化空間小  | 
  | 設(shè)置并發(fā)標(biāo)記階段的線程數(shù)  | 對(duì)本問(wèn)題中的 Young GC 暫停時(shí)間無(wú)直接影響  | 
由于索引復(fù)制本身是一個(gè)內(nèi)存密集型操作,受限于硬件和內(nèi)存帶寬,單純調(diào)整線程數(shù)或目標(biāo)停頓時(shí)間收效甚微。
4.策略四:低停頓收集器 ZGC
此前基于 G1GC 的優(yōu)化都是在傳統(tǒng)垃圾回收器的框架內(nèi)進(jìn)行修補(bǔ)。無(wú)論是 G1 還是更早的 CMS,其核心停頓(STW)根源在于對(duì)象移動(dòng)階段必須暫停所有應(yīng)用線程。對(duì)于需要移動(dòng)數(shù)百M(fèi)B存活數(shù)據(jù)的大索引場(chǎng)景,這種停頓幾乎是不可避免的。
JDK 11 引入的 ZGC 旨在從根本上解決這一問(wèn)題。其核心突破在于引入了著色指針(Colored Pointers) 和讀屏障(Load Barriers) 機(jī)制,實(shí)現(xiàn)了并發(fā)轉(zhuǎn)移。這意味著 ZGC 可以在應(yīng)用程序線程正常運(yùn)行的同時(shí),在后臺(tái)移動(dòng)和整理內(nèi)存中的對(duì)象。
其工作原理可簡(jiǎn)要概括為:
(1) 當(dāng) ZGC 需要移動(dòng)一個(gè)對(duì)象時(shí),它開(kāi)始復(fù)制數(shù)據(jù),但舊地址依然暫時(shí)有效。
(2) 任何應(yīng)用程序線程在訪問(wèn)對(duì)象時(shí)都會(huì)觸發(fā)“讀屏障”。
(3) 讀屏障會(huì)檢查該對(duì)象是否正在被移動(dòng)。如果是,它會(huì)自動(dòng)將指針“轉(zhuǎn)發(fā)”到對(duì)象的新地址(也就是指針自愈),確保應(yīng)用程序總是訪問(wèn)到正確的數(shù)據(jù)。
flowchart TD
    A[應(yīng)用線程訪問(wèn)對(duì)象] --> B[觸發(fā)讀屏障]
    B --> C{對(duì)象是否正在移動(dòng)?}
    C -- 是 --> D[由讀屏障處理<br>等待完成或轉(zhuǎn)發(fā)至新地址]
    C -- 否 --> E[直接訪問(wèn)]
    D --> F[正常讀取數(shù)據(jù)]
    E --> F這與 G1 必須“停止世界→移動(dòng)對(duì)象→更新所有指針→恢復(fù)世界”的串行化流程形成了鮮明對(duì)比,理論上的停頓時(shí)間優(yōu)勢(shì)巨大。
將應(yīng)用升級(jí)至 JDK 11 并啟用 ZGC (-XX:+UseZGC) 后,效果立竿見(jiàn)影。服務(wù)成功率進(jìn)一步提升至 99.5%。
然而,在索引切換的極短時(shí)間內(nèi),監(jiān)控系統(tǒng)依然捕捉到了輕微的響應(yīng)時(shí)間毛刺(RT尖刺)。分析 ZGC 日志,我們發(fā)現(xiàn)其根源并非長(zhǎng)時(shí)間的 STW,而是一種稱為 “分配停滯(Allocation Stall)” 的現(xiàn)象。
Allocation Stall:當(dāng)應(yīng)用程序線程試圖分配新對(duì)象(如執(zhí)行
new語(yǔ)句),但當(dāng)前堆內(nèi)存中已無(wú)足夠的可用空間時(shí),該線程會(huì)被迫暫停(“停滯”),直到 ZGC 的垃圾回收周期完成并釋放出足夠的內(nèi)存后,才能繼續(xù)執(zhí)行分配操作??梢酝ㄋ椎乩斫鉃椋骸熬€程急著要內(nèi)存,但內(nèi)存沒(méi)了,只能停下來(lái)等GC打掃完房間再繼續(xù)”。
結(jié)合系統(tǒng)監(jiān)控可以發(fā)現(xiàn),每次索引切換構(gòu)建約 500MB 新對(duì)象時(shí),都會(huì)引發(fā)一次內(nèi)存占用的瞬時(shí)尖峰,而每一次尖峰都精確對(duì)應(yīng)了一次服務(wù)的 RT 毛刺。如下圖所示:
ZGC 成功地解決了由對(duì)象復(fù)制引發(fā)的長(zhǎng)時(shí)間 STW 停頓,這是本次優(yōu)化中最顯著的進(jìn)步。然而,由于索引構(gòu)建會(huì)產(chǎn)生瞬時(shí)巨大的內(nèi)存分配需求,超出了 ZGC 即時(shí)回收的吞吐能力,從而引發(fā)了短暫的 Allocation Stall(分配停滯)。這成為了系統(tǒng)在極致性能追求下,剩余的一個(gè)微小但可感知的抖動(dòng)來(lái)源。
六、追求極致:實(shí)現(xiàn)索引無(wú)感切換的終極方案
經(jīng)過(guò)一系列 JVM 層面的調(diào)優(yōu),我們將服務(wù)成功率從最初的 95% 提升至 99.5%,成效顯著。
- MaxTenuringThreshold=0:提升至 95%
 - 升級(jí)ZGC :提升至99.5%
 
然而,對(duì)于追求極致穩(wěn)定性的系統(tǒng)而言,剩余的 0.5% 的輕微抖動(dòng)依然是亟待解決的問(wèn)題。
究其根本,只要大索引的復(fù)制發(fā)生在服務(wù)接流期間,就存在引發(fā)延遲尖刺的風(fēng)險(xiǎn)。最終的解決思路不再是“優(yōu)化復(fù)制過(guò)程”,而是“讓復(fù)制在無(wú)人感知時(shí)發(fā)生”。
1.思路轉(zhuǎn)變:從優(yōu)化GC到規(guī)避GC
既然 JVM 層面始終避免不了 1 次大索引復(fù)制,那能否避其鋒芒,新的方案是:進(jìn)行服務(wù)斷流,在斷流期間主動(dòng)觸發(fā)并完成索引的復(fù)制晉升過(guò)程。待服務(wù)重新接流時(shí),年輕代中已無(wú)大對(duì)象,后續(xù)所有 Young GC 都將是毫秒級(jí)的快速回收。這樣可以根治GC尖刺
運(yùn)維平臺(tái)提供的灰度斷流發(fā)布模式為此方案提供了基礎(chǔ):每次只發(fā)布一批機(jī)器,并在其索引加載和切換期間切斷流量,切換完成后再重新接入流量。
然而,僅依靠斷流發(fā)布并不足夠。因?yàn)樗饕姆峙洳灰欢〞?huì)立即觸發(fā) YGC——只有在 Eden 區(qū)空間不足時(shí)才會(huì)觸發(fā)。
為了更清晰地說(shuō)明單純依賴“灰度斷流”發(fā)布策略的局限性,我們以一個(gè)具體的環(huán)境配置為例進(jìn)行分析:假設(shè) Eden 區(qū)大小為 3GB,待加載的新索引約為 1GB。索引切換前 Eden 區(qū)的初始占用情況,將直接決定發(fā)布時(shí)是否會(huì)遇到問(wèn)題。
Case 1:初始占用低,隱患潛伏
索引切換前,Eden 區(qū)僅占用了 1GB,剩余空間充足。
加載 1GB 新索引后,Eden 區(qū)總占用上升至 2GB,仍未達(dá)到 3GB 的容量上限。因此,整個(gè)過(guò)程不會(huì)觸發(fā) Young GC。
當(dāng)服務(wù)重新接流后,業(yè)務(wù)請(qǐng)求產(chǎn)生的對(duì)象會(huì)迅速占滿 Eden 區(qū)剩余空間,此時(shí) 200ms 長(zhǎng)暫停勢(shì)必導(dǎo)致業(yè)務(wù)請(qǐng)求超時(shí)報(bào)錯(cuò)。如下圖:
image-20251011181411847
在此場(chǎng)景下,斷流發(fā)布沒(méi)有起到任何規(guī)避風(fēng)險(xiǎn)的作用,系統(tǒng)抖動(dòng)依然會(huì)發(fā)生。
Case 2:初始占用高,部分緩解
索引切換前,Eden 區(qū)已占用較高空間,例如 2.5GB。
在加載 1GB 新索引的過(guò)程中,Eden 區(qū)空間很快被耗盡,從而提前觸發(fā)了一次 Young GC。這次 GC 會(huì)將新索引的一部分(例如 500MB)復(fù)制到老年代。
這相當(dāng)于將一次大的復(fù)制操作拆分成兩次較小的操作,一定程度上緩解了后續(xù) GC 的停頓時(shí)間。但其效果依然不理想:
image-20251011181552791
此場(chǎng)景下,斷流發(fā)布僅能部分緩解問(wèn)題。復(fù)制操作雖被拆分但未消除,服務(wù)接流后可能仍會(huì)有一次較大的停頓。
通過(guò)以上分析可見(jiàn),單純依賴斷流發(fā)布,其效果具有極大的偶然性,嚴(yán)重依賴于發(fā)布時(shí) Eden 區(qū)的初始狀態(tài)。我們將“緩解程度”定義為在斷流期間能提前晉升到老年代的索引比例,那么它與 Eden 區(qū)初始占用的關(guān)系如下圖所示:
image-20251011181750127
如圖所示,只有當(dāng)一個(gè)批次的 Eden 初始占用較高(落入右側(cè)紅色區(qū)域)時(shí),該批次的發(fā)布才能獲得部分緩解。這意味著整個(gè)發(fā)布過(guò)程的效果是不均勻、不可控的。要實(shí)現(xiàn)徹底的、確定的“無(wú)感切換”,必須引入更主動(dòng)的干預(yù)手段。
2.終極方案:斷流發(fā)布 + 主動(dòng)預(yù)熱
前述分析表明,依賴系統(tǒng)自然狀態(tài)是不可靠的。要實(shí)現(xiàn)確定的“無(wú)感切換”,必須采取主動(dòng)干預(yù)策略。我們的核心思路是:在可控的斷流窗口期內(nèi),主動(dòng)制造一次“可控的危機(jī)”,強(qiáng)制觸發(fā)一次 Young GC,確保新索引100%在此刻被復(fù)制到老年代。
類似“預(yù)熱”的思路,每次新索引切換后、重新接流前,主動(dòng)、快速地在 Eden 區(qū)創(chuàng)建大量臨時(shí)的、短命的“預(yù)熱對(duì)象”,瞬間耗盡 Eden 區(qū)的剩余空間。此舉必然會(huì)立即觸發(fā)一次 Young GC。
這次 GC 會(huì)帶來(lái)兩個(gè)確定的結(jié)果:
(1) 回收所有無(wú)用的“預(yù)熱對(duì)象”。
(2) 將唯一存活的大型對(duì)象——新索引,復(fù)制到老年代。
當(dāng)服務(wù)重新接流時(shí),年輕代(Eden 和 Survivor 區(qū))已是“空城”,只剩下即將被快速回收的業(yè)務(wù)小對(duì)象,從此再無(wú)長(zhǎng)停頓之憂。
詳細(xì)流程如下圖所示:
image-20251011182241975
image-20251011182302673
該方案的優(yōu)勢(shì)在于,其核心邏輯僅需在關(guān)鍵的索引切換方法中增加一個(gè)簡(jiǎn)短的循環(huán)即可實(shí)現(xiàn),無(wú)需復(fù)雜架構(gòu)改造。代碼如下:
public boolean switchIndex(String indexPath){
    try {
        // 1.【斷流】加載新索引
        MyIndex newIndex = loadIndex(indexPath);
        // 2.【斷流】索引切換
        this.index = newIndex;
        // 3.【斷流】Eden 區(qū)預(yù)熱
        for (int i = 0; i < 10000; i++) {
            char[] tempArr = newchar[524288];
        }
        // 4.【斷流】通知上層索引切換完成
        return true;
        // 5.【接流】重新接流,此后 YGC 都會(huì)很快
    } catch (Exception e) {
        return false;
    }
}實(shí)現(xiàn)注意:預(yù)熱對(duì)象的大小需精心設(shè)計(jì)。單個(gè)對(duì)象應(yīng)顯著大于通常的業(yè)務(wù)對(duì)象,以快速消耗內(nèi)存,但又必須小于
G1HeapRegionSize的一半(通常為 1MB),以避免被 G1GC 直接當(dāng)作大對(duì)象(Humongous Object)分配至老年代,從而繞過(guò)年輕代,使預(yù)熱機(jī)制失效。
3.效果驗(yàn)證
部署并啟用“主動(dòng)預(yù)熱”方案后,我們通過(guò)以下三個(gè)維度對(duì)優(yōu)化效果進(jìn)行了全面驗(yàn)證,結(jié)果令人振奮。
1)GC 日志分析:復(fù)制操作被成功前置
對(duì)比優(yōu)化前后的 GC 日志,變化一目了然。下圖所示的優(yōu)化后日志清晰記錄了斷流期間發(fā)生的完整過(guò)程:
圖片
關(guān)鍵節(jié)點(diǎn)解讀:
① 索引加載:新索引被創(chuàng)建,占據(jù) Eden 區(qū)大部分空間。
② 主動(dòng)預(yù)熱觸發(fā) YGC:預(yù)熱代碼循環(huán)創(chuàng)建大量臨時(shí)對(duì)象,迅速耗盡 Eden 區(qū)剩余空間,迫使 JVM 立即觸發(fā)一次 Young GC。
③ 索引晉升:本次 YGC 將新索引(存活對(duì)象)完整地復(fù)制到老年代,同時(shí)回收所有臨時(shí)預(yù)熱對(duì)象。
④ 接流后常態(tài):服務(wù)重新接流后,所有的 Young GC 都只用于回收業(yè)務(wù)請(qǐng)求產(chǎn)生的瞬時(shí)小對(duì)象,耗時(shí)均下降至毫秒級(jí),變得快速而平穩(wěn)。
2)系統(tǒng)監(jiān)控:消失的抖動(dòng)曲線
監(jiān)控系統(tǒng)是最直觀的成效證明。下圖展示了優(yōu)化后一段時(shí)間內(nèi)的服務(wù)響應(yīng)時(shí)間(RT)監(jiān)控曲線,紅色箭頭標(biāo)注了數(shù)次索引切換事件的發(fā)生時(shí)刻。
可以觀察到,在索引切換時(shí),RT 曲線依然平整,幾乎沒(méi)有出現(xiàn)任何毛刺或尖峰。這表明索引切換帶來(lái)的延遲影響已經(jīng)被完全控制在斷流期內(nèi),對(duì)線上業(yè)務(wù)流量做到了真正的“無(wú)感”。
3)業(yè)務(wù)指標(biāo):達(dá)到極致穩(wěn)定
最終,一切優(yōu)化都體現(xiàn)在業(yè)務(wù)結(jié)果上。如下圖所示,經(jīng)過(guò)本輪徹底優(yōu)化,服務(wù)的日常成功率穩(wěn)定在 99.995% 以上,剩余極少數(shù)的失敗通常源于網(wǎng)絡(luò)抖動(dòng)等外部因素,GC 引發(fā)的服務(wù)抖動(dòng)問(wèn)題已被完全根治。
歷經(jīng)從 JVM 參數(shù)調(diào)優(yōu)到垃圾收集器升級(jí),最終到“發(fā)布策略+主動(dòng)預(yù)熱”的架構(gòu)與流程優(yōu)化,我們成功地將一個(gè)因固有業(yè)務(wù)模式(定期加載大索引)而引發(fā)的性能瓶頸徹底化解,實(shí)現(xiàn)了技術(shù)上的極致追求。
七、總結(jié)
本文針對(duì)一個(gè)高并發(fā)(QPS 10W+)、低延遲(要求毫秒級(jí)響應(yīng))、高內(nèi)存壓力(每15分鐘需切換GB級(jí)索引)的服務(wù)系統(tǒng),因其頻繁的索引切換導(dǎo)致的GC尖刺和系統(tǒng)抖動(dòng)問(wèn)題,進(jìn)行了一系列從JVM層到架構(gòu)層的深度優(yōu)化實(shí)踐。
我們系統(tǒng)地探索了多種解決方案,其效果對(duì)比如下:
優(yōu)化手段  | 服務(wù)可用率(抖動(dòng)時(shí))  | 
G1GC + 默認(rèn)參數(shù)  | 95%(Baseline)  | 
-XX:MaxTenuringThreshold=0  | 98%  | 
-XX:InitialTenuringThreshold=1  | 98%  | 
-XX:+AlwaysTenure  | 98%  | 
ZGC + 默認(rèn)參數(shù)  | 99.5%  | 
G1GC + 灰度斷流 + Eden預(yù)熱  | 99.995%  | 
至此,未來(lái)無(wú)論系統(tǒng) QPS 漲到多高、索引體積膨脹到多大、索引切換多么頻繁,系統(tǒng)都能無(wú)感切換索引,穩(wěn)定性不再受到任何影響。















 
 
 










 
 
 
 