偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

京東面試:什么是gc尖刺? 怎么解決由于 gc 導(dǎo)致的尖刺?

開(kāi)發(fā) 前端
系統(tǒng)在索引發(fā)布后,新生成的索引對(duì)象在Young GC中反復(fù)復(fù)制,且由于對(duì)象數(shù)量大、存活時(shí)間長(zhǎng),導(dǎo)致Copy階段STW過(guò)長(zhǎng),業(yè)務(wù)線程暫停,上游超時(shí)增多。

尼恩說(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-20251011101749451image-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-20251011164004333image-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-20250929131905130image-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-20251011171830395image-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-20251011172819461image-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-20251011173847017image-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è)效果

-XX:MaxGCPauseMillis

設(shè)置 G1 的目標(biāo)最大停頓時(shí)間

效果不明顯。目標(biāo)停頓僅是期望,無(wú)法突破物理限制

-XX:ParallelGCThreads

設(shè)置 STW 階段并行 GC 的線程數(shù)

效果不明顯。默認(rèn)值已接近核心數(shù),優(yōu)化空間小

-XX:ConcGCThreads

設(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-20251011181411847image-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-20251011181552791image-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-20251011181750127image-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-20251011182241975image-20251011182241975

image-20251011182302673image-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)定性不再受到任何影響。

責(zé)任編輯:武曉燕 來(lái)源: 技術(shù)自由圈
相關(guān)推薦

2012-08-16 10:43:10

GC

2023-12-04 10:36:46

SessionCookie

2023-10-04 19:43:38

2024-05-21 09:08:57

JVM調(diào)優(yōu)面試

2024-05-24 10:36:27

2023-12-07 12:21:04

GCJVM垃圾

2019-11-27 14:41:50

Java技術(shù)語(yǔ)言

2024-02-26 11:03:05

golang緩存數(shù)據(jù)庫(kù)

2017-06-09 08:49:07

加載器Full GCJVM

2025-06-03 07:00:00

大數(shù)據(jù)Flink并行度

2021-09-29 09:24:21

GCGo STW

2017-04-19 12:09:56

數(shù)組動(dòng)態(tài)擴(kuò)容GC

2017-06-27 08:41:04

JVM設(shè)計(jì)缺陷GC

2014-05-08 14:13:00

Java面向GC

2023-08-26 19:23:40

Javastatic關(guān)鍵字

2022-07-19 07:02:47

JVMGC分代收集

2017-04-17 11:07:19

GC數(shù)組動(dòng)態(tài)擴(kuò)容

2020-03-03 17:35:09

Full GCMinor

2024-02-05 19:06:04

DartVMGC流程

2017-05-18 15:02:36

AndroidGC原理JVM內(nèi)存回收
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)