精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

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

尼恩說在前面

在40歲老架構(gòu)師 尼恩的讀者交流群(50+)中,最近有小伙伴拿到了一線互聯(lián)網(wǎng)企業(yè)如得物、阿里、滴滴、極兔、有贊、希音、百度、網(wǎng)易、美團的面試資格,遇到很多很重要的面試題:

  • 什么是gc尖刺? 怎么  解決由于 gc 導(dǎo)致的 尖刺?
  • GC 毛刺見過嗎, 如何排查?

最近有小伙伴在面試京東、  阿里、希音等大廠,又遇到了相關(guān)的面試題。

小伙伴 沒系統(tǒng)梳理, 支支吾吾的說了幾句,面試官不滿意, 掛了

特別說明:

GC 毛刺 很多的場景。

下面的大廠 案例, 介紹的是 一個場景,這個場景是:   大規(guī)模的長命小對象 , 在 新生代 ygc 頻繁復(fù)制,導(dǎo)致的GC 毛刺。

特別說明:

其他的GC 毛刺 場景 后面尼恩再找一些案例來進行展示。

本文的案例 ,以及其他的gc 毛刺案例, 都來自互聯(lián)網(wǎng),不是尼恩原創(chuàng) 案例。

如果原作者不愿意 尼恩用來作為 學(xué)習(xí)材料 放在公眾號, 可以找尼恩反饋,尼恩立即從本公眾號撤下來。

一、什么是gc尖刺?

GC尖刺(Garbage Collection Spike) ,有時也被稱為GC毛刺或GC突刺 , 并不是某個官方術(shù)語,而是線上運維的“體感”說法。

大概意思是: 在一條本來平穩(wěn)的 RT(響應(yīng)時間)或 CPU 曲線上,突然豎起一根像刺一樣的尖峰,持續(xù)時間從幾十毫秒到幾秒不等,看上去很多 突刺。

gc尖刺 根因 是: 垃圾回收器在某一刻發(fā)生了長時間停頓(Stop-The-World,簡稱 STW)。

由于Stop-The-World(STW) 暫停,導(dǎo)致應(yīng)用程序 RT(響應(yīng)時間)或 CPU 曲線上 出現(xiàn)的突然而顯著的峰值

簡單來說,就是GC過程中,JVM會暫停所有應(yīng)用線程來執(zhí)行垃圾回收,如果這次暫停時間過長,就會像路上的突然堵車一樣,導(dǎo)致系統(tǒng)性能出現(xiàn)瞬間的“卡頓”。

GC尖刺的背后,往往是內(nèi)存管理不當(dāng)或垃圾回收器配置不佳。

一些典型GC尖刺誘因:

1、內(nèi)存分配問題

  • 短命大對象:在循環(huán)或高頻方法中持續(xù)創(chuàng)建大對象(如大的數(shù)組、集合),這些對象可能迅速占滿新生代,導(dǎo)致Minor GC頻繁,且每次回收耗時增加。更糟的是,如果大對象過早晉升到老年代,還會引發(fā)不必要的Full GC,導(dǎo)致gc 尖刺。
  • 內(nèi)存泄漏:由于代碼缺陷(如未清理的靜態(tài)集合、未關(guān)閉的資源、ThreadLocal使用不當(dāng)),導(dǎo)致對象無法被回收。老年代內(nèi)存被無效對象逐漸填滿,最終觸發(fā)長時間停頓的Full GC,但回收效果甚微,內(nèi)存使用率居高不下,導(dǎo)致gc 尖刺。
  • 大規(guī)模的長命小對象在年輕代復(fù)制: 本文的例子中,出現(xiàn)了  大規(guī)模長命小對象(約 500MB),在年輕代的 eden和 幸存者區(qū)來回復(fù)制,導(dǎo)致gc 尖刺。

2、垃圾回收器配置與選擇

  • 堆內(nèi)存設(shè)置不合理:堆內(nèi)存過小會導(dǎo)致GC頻繁發(fā)生;堆內(nèi)存過大則會使單次GC需要處理的數(shù)據(jù)量增多,可能導(dǎo)致STW時間變長。
  • GC參數(shù)不匹配:例如,G1垃圾回收器的 MaxGCPauseMillis(預(yù)期最大停頓時間)設(shè)置過小,可能會迫使GC更頻繁地工作以試圖達(dá)到目標(biāo),反而影響整體吞吐量并可能引發(fā)問題。
  • GC器選擇不當(dāng):像ZGC和ShenandoahGC這類低停頓回收器,雖然STW時間極短,但在高吞吐量計算密集型場景下,其并發(fā)執(zhí)行會與業(yè)務(wù)線程競爭CPU資源,可能導(dǎo)致整體響應(yīng)時間上升和周期性尖刺。

3、其他問題:如系統(tǒng)資源與外部因素

  • 日志打印過量:大量同步日志寫入會爭搶磁盤I/O鎖,導(dǎo)致線程阻塞。同時,日志文件快速增大觸發(fā)的滾動清理操作也會消耗大量CPU和I/O資源,間接引發(fā)或加劇GC壓力。
  • 定時任務(wù)處理大數(shù)據(jù)集:定時任務(wù)一次性加載和處理大量數(shù)據(jù)(如從數(shù)據(jù)庫撈出數(shù)十萬條記錄),會在短時間內(nèi)產(chǎn)生海量對象,給GC帶來巨大壓力。

GC尖刺的危害:

GC尖刺的危害是直接且嚴(yán)重的,尤其在高并發(fā)、低延遲要求的系統(tǒng)中:

  • 接口響應(yīng)時間劇烈抖動:最直接的表現(xiàn)就是應(yīng)用服務(wù)的P99、P999延遲(如99%或99.9%請求的響應(yīng)時間)出現(xiàn)周期性或突發(fā)性的尖峰,導(dǎo)致用戶體驗下降。
  • 系統(tǒng)吞吐量下降:頻繁且長時間的GC會占用大量系統(tǒng)資源(CPU資源被大量用于垃圾回收而非業(yè)務(wù)處理),導(dǎo)致系統(tǒng)整體處理能力(QPS/TPS)降低。
  • 上游調(diào)用超時與故障擴散:若GC導(dǎo)致服務(wù)響應(yīng)超時,可能引起上游調(diào)用方(如網(wǎng)關(guān)、其他微服務(wù))連鎖超時失敗,在分布式系統(tǒng)中可能引發(fā)雪崩效應(yīng)。

二、問題復(fù)盤

在高并發(fā)、低延遲的服務(wù)中,GC 的行為會直接影響服務(wù)的響應(yīng)時間和穩(wěn)定性。

本文場景  討論的場景, 是 源于一個真實的大廠 高并發(fā)系統(tǒng)(系統(tǒng)A),該系統(tǒng)的 QPS 日常在十萬級別,大促期間甚至?xí)^ 40W,且對響應(yīng)時間有毫秒級的嚴(yán)格要求。

任何由于GC 垃圾回收引起的停頓, 都可能導(dǎo)致超時和業(yè)務(wù)成功率下降。

image-20251011101749451image-20251011101749451

在大促期間(QPS 40W),的巡檢監(jiān)控中,發(fā)現(xiàn)上游調(diào)用方出現(xiàn)零星超時告警。

通過監(jiān)控系統(tǒng)定位到系統(tǒng)A在特定時段出現(xiàn)了周期性響應(yīng)時間毛刺(如下圖所示),這些毛刺與GC日志中的Full GC時間點高度吻合,初步判斷是GC停頓引發(fā)了服務(wù)抖動。

圖片圖片

問題根因:大規(guī)模 長生命小對象引發(fā)新生代復(fù)制風(fēng)暴。

先說結(jié)論:我們發(fā)現(xiàn)系統(tǒng)A中緩存了一批業(yè)務(wù)索引數(shù)據(jù),這些數(shù)據(jù)具有以下特點。

  • 大規(guī)模  小對象:  體積小(每個對象幾KB至幾十KB)、總量大(約500MB)
  • 長生命對象 :一旦加載,長時間存活(通常貫穿整個服務(wù)生命周期)
  • 在業(yè)務(wù)邏輯中頻繁被使用

默認(rèn)情況下,這些對象會在新生代的 Eden 區(qū)創(chuàng)建,由于存活時間長,它們會在 Survivor 區(qū)來回復(fù)制,直至年齡達(dá)到閾值后才被晉升到老年代。

在這個過程中,會產(chǎn)生兩方面開銷:

(1) 復(fù)制開銷:大規(guī)模對象在 Survivor 區(qū)之間來回復(fù)制,CPU 消耗顯著;

(2) 晉升開銷:對象年齡達(dá)到閾值時,批量復(fù)制到老年代,容易引發(fā)停頓。

尤其是在 Survivor 區(qū)空間不足或?qū)ο髲?fù)制頻率較高時,Young GC 耗時明顯增加,嚴(yán)重時甚至?xí)|發(fā)提前晉升或直接進入老年代,引發(fā)了GC尖刺問題。

解決這類問題,大致分為以下三步處理:

  • 排查問題:定位根因
  • 分析問題:找到解決方案
  • 優(yōu)化過程:解決問題

優(yōu)化的思路:  盡早晉升,也就是  讓 大規(guī)模的長命小對象(業(yè)務(wù)索引數(shù)據(jù))盡早晉升到老年代,  或者 讓索引直接分配到老年代,從而加速 加速索引復(fù)制。  當(dāng)然, 也會考慮 升級 GC , 升級通過  斷流發(fā)布 +主動預(yù)熱 規(guī)避GC。  接下來和大家一一介紹。

在不加一臺機器、不改變流量大小的前提下,系統(tǒng)成功率(抖動時)逐步優(yōu)化效果為:95% => 98% => 99.5% => 99.995%,保障系統(tǒng)高可用。

下面將詳細(xì)介紹整個排查和優(yōu)化過程。

三、排查過程

1、初步常規(guī)分析

首先從上游業(yè)務(wù)報警入手,發(fā)現(xiàn)報錯均為同步調(diào)用超時(TimeoutException),因此聚焦系統(tǒng)A自身狀態(tài),開展第一輪排查:

  • 對比故障時間點前后流量監(jiān)控,未見明顯峰值,CPU使用率和系統(tǒng)負(fù)載均處于正常水位,可排除流量激增導(dǎo)致過載的可能。
  • 系統(tǒng)A為純內(nèi)存計算型服務(wù),無數(shù)據(jù)庫、緩存或RPC調(diào)用,不存在外部組件拖慢整體響應(yīng)的因素。
  • 系統(tǒng)雖高并發(fā),但請求間無同步互斥邏輯,不存在分布式鎖或線程鎖競爭導(dǎo)致的阻塞超時。

經(jīng)過首輪排查,已排除流量激增、外部服務(wù)有瓶頸、并發(fā)鎖等可能影響因素,但并未定位到根因,需進一步向內(nèi)挖掘系統(tǒng)自身狀態(tài)。

2、定位根因

在排除常規(guī)疑點后,我們開始查看系統(tǒng)內(nèi)部日志與監(jiān)控,發(fā)現(xiàn)關(guān)鍵日志證據(jù):發(fā)現(xiàn)系統(tǒng)在抖動發(fā)生前執(zhí)行了一次索引發(fā)布。(熱更新),如下圖所示:

圖片圖片

說明:該系統(tǒng)每隔15分鐘會全量替換內(nèi)存中的業(yè)務(wù)索引(一個約500MB的復(fù)雜Map結(jié)構(gòu)),此過程瞬間產(chǎn)生大量新對象。

檢查對應(yīng)時間點的GC日志,發(fā)現(xiàn)Young GC耗時異常,其中Object Copy階段耗時超過200ms,如下圖:

圖片圖片

Object Copy是YGC的關(guān)鍵階段:存活對象會從Eden區(qū)復(fù)制到Survivor區(qū)(或晉升老年代)。大量存活對象導(dǎo)致復(fù)制開銷陡增,引發(fā)GC尖刺。

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

也就是:大規(guī)模長生命對象在新生代頻繁復(fù)制,引起GC停頓放大,最終導(dǎo)致服務(wù)超時

四、問題的定位與分析

1、常規(guī)優(yōu)化思路分析

面對GC暫停時間過長的問題,通常有以下幾種優(yōu)化思路:

image-20251011164004333image-20251011164004333

然而,在本次場景中,上述常規(guī)方案大多難以直接應(yīng)用或效果有限。

原因如下:

  • 首先,經(jīng)過細(xì)致排查,代碼層面并未發(fā)現(xiàn)明顯缺陷,索引結(jié)構(gòu)也已高度壓縮,沒有進一步優(yōu)化的空間。同時,受限于業(yè)務(wù)特性,索引更新機制必須采用全量替換,無法實現(xiàn)增量更新。
  • 其次,單純增加機器數(shù)量雖然可以通過分流請求來減少單機在STW期間影響的請求量,但這本質(zhì)上是一種規(guī)避而非解決,不僅無法從根本上消除GC停頓,還會造成資源利用率下降和成本上升。
  • 此外,堆外內(nèi)存方案雖然能規(guī)避GC管理,但需要頻繁的序列化和反序列化操作。在高并發(fā)訪問的場景下,這部分額外開銷對延遲的影響無法忽視,與系統(tǒng)所需的毫秒級響應(yīng)目標(biāo)相悖。

因此,綜合評估后,我們決定將優(yōu)化重點放在JVM參數(shù)調(diào)優(yōu)上:通過精細(xì)調(diào)整垃圾回收器的行為模式,優(yōu)化內(nèi)存分配和晉升策略,盡可能降低大規(guī)模對象復(fù)制帶來的負(fù)面影響,從而在現(xiàn)有架構(gòu)下保障服務(wù)的高可用性。

2、GC日志深度解析與根因推演

基于前期分析,問題的核心在于YGC的Object Copy階段:大規(guī)模索引對象的復(fù)制操作耗時過長,導(dǎo)致STW時間增加,進而引發(fā)上游請求超時。本節(jié)將通過詳細(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)最大暫停時間100ms
-XX:InitiatingHeapOccupancyPercent=45 # 老年代占用45%時啟動混合GC
-XX:+HeapDumpOnOutOfMemoryError  # OOM時生成堆轉(zhuǎn)儲
-XX:MaxDirectMemorySize=1g       # 最大直接內(nèi)存限制1GB

通過內(nèi)部監(jiān)控平臺ATP對GC日志進行可視化分析,下圖中標(biāo)出了各 GC 事件的時間點和變化曲線:

圖片圖片

圖中清晰展示了以下關(guān)鍵信息:

① 藍(lán)色圓點(YGC事件):每個圓點代表一次Young GC事件。圖中可見大量密集分布的藍(lán)點,表明YGC發(fā)生頻率高且耗時極短(毫秒級),能夠迅速完成年輕代垃圾回收——這是高吞吐、低延遲系統(tǒng)的理想表現(xiàn),符合預(yù)期。

② 粉色折線(堆內(nèi)存占用):該折線反映堆內(nèi)存使用量的動態(tài)變化,呈現(xiàn)規(guī)律的鋸齒形態(tài)——快速上升后驟降。這種模式符合預(yù)期:由于系統(tǒng)流量大,請求處理過程中會持續(xù)產(chǎn)生大量短期存活的臨時對象,使內(nèi)存占用快速上升;當(dāng)Eden區(qū)空間不足時觸發(fā)YGC,迅速回收這些對象,使內(nèi)存占用回落至低點。

③ 異常藍(lán)點(長耗時YGC):部分藍(lán)點明顯遠(yuǎn)離橫軸,表示這些YGC的耗時顯著高于正常水平。這些點與之前在日志中手動識別出的長耗時記錄相符,是導(dǎo)致服務(wù)抖動的直接原因,需要重點關(guān)注。

④ 紫色折線(老年代占用):該曲線反映老年代內(nèi)存使用情況。正常情況下,因絕大多數(shù)對象在年輕代就被回收,老年代占用率增長緩慢。但值得關(guān)注的是,每次長耗時YGC出現(xiàn)時,紫色折線都呈現(xiàn)明顯的階梯式躍升,表明此時有大量對象晉升至老年代,這一現(xiàn)象需要重點關(guān)注。

進一步觀察發(fā)現(xiàn),長耗時YGC總是成對出現(xiàn),且具有“第一次晉升量少、第二次晉升量多”的規(guī)律,如下圖所示:

圖片圖片

綜合所有線索,可以完整推演出問題發(fā)生的過程:

階段一:系統(tǒng)創(chuàng)建新索引對象(約500MB),這些對象被分配在Eden區(qū)

階段二:Eden區(qū)空間不足觸發(fā)第一次YGC,新索引作為存活對象被復(fù)制到Survivor區(qū),大量對象復(fù)制導(dǎo)致STW長達(dá)200ms+

階段三:業(yè)務(wù)代碼完成索引切換,將GcRoot指向新索引,同時斷開舊索引引用

階段四:再次觸發(fā)YGC,新索引從Survivor區(qū)晉升到老年代,舊索引被回收,再次產(chǎn)生200ms+ STW

階段五:新索引穩(wěn)定存在于老年代,后續(xù)YGC只需處理小對象,恢復(fù)毫秒級響應(yīng)

通過這一分析,我們準(zhǔn)確定位了GC尖刺的根本原因:大規(guī)模長生命周期對象在年輕代經(jīng)歷了兩次完整的復(fù)制過程(Survivor區(qū)復(fù)制和老年代晉升),導(dǎo)致雙倍的STW停頓時間。

image-20250929131905130image-20250929131905130

五、優(yōu)化過程

在明確了問題根源——每次新建的大索引對象在年輕代中經(jīng)歷多次復(fù)制,引發(fā)長時間 Young GC 停頓——之后,

我們圍繞減少復(fù)制次數(shù)、降低暫停時間的目標(biāo),設(shè)計了如下優(yōu)化方案。

image-20251011171830395image-20251011171830395

1.策略一:讓索引盡早晉升至老年代

默認(rèn)情況下,新創(chuàng)建的對象在經(jīng)歷一定次數(shù)的 Young GC 后才會晉升到老年代。我們的核心思路是改寫這個流程,讓大索引以最快路徑進入老年代,避免在年輕代中反復(fù)復(fù)制

1.1 調(diào)整晉升閾值:MaxTenuringThreshold

MaxTenuringThreshold參數(shù)用于設(shè)定對象在晉升至老年代前,能在年輕代中經(jīng)歷的最大 GC 次數(shù)。默認(rèn)值通常為 15,意味著對象需要在 Survivor 區(qū)之間來回拷貝多次,才有可能晉升。

通過分析線上 GC 日志,我們發(fā)現(xiàn) G1GC 對大型索引對象進行了優(yōu)化(Direct Tenuring)。該索引的實際流轉(zhuǎn)路徑為:Eden → S0 → Old,僅經(jīng)歷了 2 次復(fù)制,而非默認(rèn)的 15 次。這相當(dāng)于 JVM 自動將 MaxTenuringThreshold動態(tài)調(diào)整為了 1,其過程如下:

階段一:新索引在 Eden 區(qū)創(chuàng)建,年齡(Age)為 0。

階段二:發(fā)生第一次 Young GC,索引存活。由于當(dāng)前年齡(0)小于閾值(1),索引從 Eden 被復(fù)制到 S0 區(qū),年齡增長為 1。

階段三:發(fā)生第二次 Young GC,索引依然存活。此時年齡(1)等于閾值(1),索引被復(fù)制到 Old 區(qū),完成晉升。

我們嘗試手動設(shè)置 -XX:MaxTenuringThreshold=1進行驗證,GC 日志證實索引的流轉(zhuǎn)路徑仍是 Eden → S0 → Old

圖片

能不能進一步的優(yōu)化?

很容易想到,能否將閾值設(shè)置為 0,讓索引在第一次 GC 時就直接從 Eden 晉升到 Old,完全跳過 Survivor 區(qū)?

這樣,復(fù)制次數(shù)將從 2 次降為 1 次,預(yù)計暫停時間可減少近一半。

將參數(shù)修改為 -XX:MaxTenuringThreshold=0后,流程變?yōu)椋?/span>

階段一:新索引在 Eden 區(qū)創(chuàng)建,年齡為 0。

階段二:發(fā)生 Young GC,索引存活。由于當(dāng)前年齡(0)已等于閾值(0),索引被直接復(fù)制到 Old 區(qū)。

實驗結(jié)果的 GC 日志顯示,Young GC 后年輕代的使用量驟降至接近零,證明大型索引已被直接晉升到老年代,優(yōu)化生效。

圖片圖片

優(yōu)化效果總結(jié):

此優(yōu)化在不修改任何業(yè)務(wù)代碼、不增加硬件成本的前提下,通過調(diào)整一個 JVM 參數(shù),便將因索引切換導(dǎo)致的長暫停 GC 次數(shù)減半。

從系統(tǒng)監(jiān)控來看,索引切換期間的報錯量顯著減少,服務(wù)成功率從 95% 提升至 98%。

image-20251011172819461image-20251011172819461

1.2 其他相關(guān)參數(shù)實驗

在通過 MaxTenuringThreshold成功優(yōu)化后,我們進一步探索了其他能控制對象晉升策略的參數(shù),以尋求更優(yōu)解或替代方案。

1) InitialTenuringThreshold

此參數(shù)與 MaxTenuringThreshold作用類似,用于設(shè)定對象晉升的初始年齡閾值。

實測表明,設(shè)置 -XX:InitialTenuringThreshold=1同樣可以將索引的復(fù)制次數(shù)從 2 次降為 1 次,優(yōu)化效果與 MaxTenuringThreshold=0類似,都能有效提升系統(tǒng)穩(wěn)定性。

圖片圖片

2) AlwaysTenure

這是一個更為極端的參數(shù),其字面含義是“總是晉升”。

開啟后,所有在 Young GC 中存活的對象都會直接晉升到老年代,完全跳過 Survivor 區(qū)。

實測設(shè)置 -XX:+AlwaysTenure后,同樣達(dá)到了減少一次復(fù)制的效果。其對象流轉(zhuǎn)路徑可概括為:

flowchart LR
    A[Eden] -- YGC / 存活 --> B[Old]

圖片

關(guān)于 AlwaysTenure 的說明:

  • 多次 GC 現(xiàn)象:因為索引對象龐大,而 Eden 區(qū)剩余空間有限,其構(gòu)建過程可能橫跨多次 Young GC。每次 GC 都會將已構(gòu)建完成的部分索引直接晉升至老年代。因此,圖中顯示經(jīng)歷了 3 次 YGC 才將完整索引搬到老年代,這與“減少單次晉升的復(fù)制次數(shù)”的結(jié)論并不矛盾。它只是將原本需要在 2 次 GC 中完成的兩次復(fù)制,變成了在 3 次 GC 中完成的三次直接晉升(每次復(fù)制一次)。
  • 設(shè)計思想:AlwaysTenure的設(shè)計理念是禁用 Survivor 區(qū),僅使用 Eden 和 Old 區(qū)。與之相反的參數(shù)是 -XX:+NeverTenure,它會試圖讓對象永遠(yuǎn)留在年輕代,禁用 Old 區(qū)。兩者都是非常極端的策略,僅適用于特定的業(yè)務(wù)場景。
  • 對老年代的影響:通常,降低晉升閾值會讓更多短期存活的對象進入老年代,從而增加 Full GC 的風(fēng)險。但我們的業(yè)務(wù)場景特殊性在于對象存活時間兩極分化:RPC 請求產(chǎn)生的臨時對象生命周期極短(毫秒級),而索引對象生命周期極長(數(shù)十分鐘以上)。因此,在 Young GC 時,臨時對象早已被回收,而索引對象是唯一需要被晉升的。修改這些參數(shù)并不會導(dǎo)致大量本應(yīng)被回收的短命對象進入老年代,故不會增加 Full GC 的負(fù)擔(dān)。

2.嘗試讓索引直接分配至老年代

通過上面調(diào)整晉升策略,我們成功地將索引的復(fù)制次數(shù)從 2 次減少到 1 次。一個更極端的想法隨之產(chǎn)生:能否讓索引在創(chuàng)建時就直接分配在老年代,實現(xiàn) 0 次復(fù)制,從而從根本上避免由復(fù)制引起的停頓?

這個理想的分配路徑如下圖所示:

image-20251011173847017image-20251011173847017

圍繞此目標(biāo),我們進行了以下兩種嘗試,但均未取得預(yù)期效果。

2.1 嘗試一:PretenureSizeThreshold

PretenureSizeThreshold是一個經(jīng)典的 JVM 參數(shù),旨在讓大于指定大小的對象直接在老年代分配,以避免在年輕代發(fā)生昂貴的復(fù)制操作。

遺憾的是,該參數(shù)在 G1 垃圾收集器下是不生效的。調(diào)整此參數(shù)后,通過監(jiān)控發(fā)現(xiàn)系統(tǒng)穩(wěn)定性指標(biāo)并無改善,GC 日志也顯示索引依然在年輕代中創(chuàng)建和流轉(zhuǎn)。

2.2 嘗試二:G1HeapRegionSize 與 Humongous Object

G1GC 有一個內(nèi)置機制用于處理大對象(Humongous Object)。它將堆劃分為多個大小相等的 Region(區(qū)域)。當(dāng)一個對象的大小超過單個 Region 容量的一半時,它就會被視為 Humongous Object,并被直接分配在老年代的特殊區(qū)域中。

理論上,通過調(diào)整 -XX:G1HeapRegionSize可以控制 Region 的大小,從而讓我們的索引滿足 Humongous Object 的條件。

我們增大了 G1HeapRegionSize以確保索引整體大小超過其一半,但優(yōu)化后系統(tǒng)在索引切換時依然出現(xiàn)抖動。分析 GC 日志,索引的分配路徑依然是 Eden → Survivor → Old,并未被識別為 Humongous Object。

根本原因在于索引對象的物理結(jié)構(gòu)與邏輯結(jié)構(gòu)上

  • 邏輯結(jié)構(gòu):從業(yè)務(wù)視角看,我們有一個約 500MB 的“大索引對象”。
  • 物理結(jié)構(gòu):但從 JVM 的內(nèi)存分配視角看,這個“大索引”實際上是由上百萬個獨立的小對象(如 Map Entry、自定義數(shù)據(jù)結(jié)構(gòu)等)在程序運行過程中逐個構(gòu)建而成的。JVM 每次通過 new關(guān)鍵字分配的是這些小型個體對象,它們的大小遠(yuǎn)小于 G1HeapRegionSize的一半,因此完全符合在 Eden 區(qū)分配的條件。

除非是像 int[] arr = new int[1000000000];這樣,在代碼層面明確聲明分配的、單一的、巨大的連續(xù)數(shù)組,JVM 才能在一次分配中就識別出其大小并將其直接作為 Humongous Object 處理。

對于由海量小對象聚合而成的邏輯大對象,無法通過調(diào)整標(biāo)準(zhǔn) JVM 參數(shù)讓其直接在老年代分配。此優(yōu)化路徑在當(dāng)前業(yè)務(wù)代碼結(jié)構(gòu)下不可行。

3.策略三:加速索引的復(fù)制過程

在不改變復(fù)制次數(shù)的情況下,我們嘗試通過調(diào)整 GC 相關(guān)參數(shù)來提升復(fù)制速度。

參數(shù)名

作用

實測效果

-XX:MaxGCPauseMillis

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

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

-XX:ParallelGCThreads

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

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

-XX:ConcGCThreads

設(shè)置并發(fā)標(biāo)記階段的線程數(shù)

對本問題中的 Young GC 暫停時間無直接影響

由于索引復(fù)制本身是一個內(nèi)存密集型操作,受限于硬件和內(nèi)存帶寬,單純調(diào)整線程數(shù)或目標(biāo)停頓時間收效甚微。

4.策略四:低停頓收集器 ZGC

此前基于 G1GC 的優(yōu)化都是在傳統(tǒng)垃圾回收器的框架內(nèi)進行修補。無論是 G1 還是更早的 CMS,其核心停頓(STW)根源在于對象移動階段必須暫停所有應(yīng)用線程。對于需要移動數(shù)百MB存活數(shù)據(jù)的大索引場景,這種停頓幾乎是不可避免的。

JDK 11 引入的 ZGC 旨在從根本上解決這一問題。其核心突破在于引入了著色指針(Colored Pointers) 和讀屏障(Load Barriers) 機制,實現(xiàn)了并發(fā)轉(zhuǎn)移。這意味著 ZGC 可以在應(yīng)用程序線程正常運行的同時,在后臺移動和整理內(nèi)存中的對象。

其工作原理可簡要概括為:

(1) 當(dāng) ZGC 需要移動一個對象時,它開始復(fù)制數(shù)據(jù),但舊地址依然暫時有效。

(2) 任何應(yīng)用程序線程在訪問對象時都會觸發(fā)“讀屏障”。

(3) 讀屏障會檢查該對象是否正在被移動。如果是,它會自動將指針“轉(zhuǎn)發(fā)”到對象的新地址(也就是指針自愈),確保應(yīng)用程序總是訪問到正確的數(shù)據(jù)。

flowchart TD
    A[應(yīng)用線程訪問對象] --> B[觸發(fā)讀屏障]
    B --> C{對象是否正在移動?}
    C -- 是 --> D[由讀屏障處理<br>等待完成或轉(zhuǎn)發(fā)至新地址]
    C -- 否 --> E[直接訪問]
    D --> F[正常讀取數(shù)據(jù)]
    E --> F

這與 G1 必須“停止世界→移動對象→更新所有指針→恢復(fù)世界”的串行化流程形成了鮮明對比,理論上的停頓時間優(yōu)勢巨大。

將應(yīng)用升級至 JDK 11 并啟用 ZGC (-XX:+UseZGC) 后,效果立竿見影。服務(wù)成功率進一步提升至 99.5%

然而,在索引切換的極短時間內(nèi),監(jiān)控系統(tǒng)依然捕捉到了輕微的響應(yīng)時間毛刺(RT尖刺)。分析 ZGC 日志,我們發(fā)現(xiàn)其根源并非長時間的 STW,而是一種稱為 “分配停滯(Allocation Stall)” 的現(xiàn)象。

Allocation Stall:當(dāng)應(yīng)用程序線程試圖分配新對象(如執(zhí)行 new語句),但當(dāng)前堆內(nèi)存中已無足夠的可用空間時,該線程會被迫暫停(“停滯”),直到 ZGC 的垃圾回收周期完成并釋放出足夠的內(nèi)存后,才能繼續(xù)執(zhí)行分配操作。可以通俗地理解為:“線程急著要內(nèi)存,但內(nèi)存沒了,只能停下來等GC打掃完房間再繼續(xù)”。

結(jié)合系統(tǒng)監(jiān)控可以發(fā)現(xiàn),每次索引切換構(gòu)建約 500MB 新對象時,都會引發(fā)一次內(nèi)存占用的瞬時尖峰,而每一次尖峰都精確對應(yīng)了一次服務(wù)的 RT 毛刺。如下圖所示:

ZGC 成功地解決了由對象復(fù)制引發(fā)的長時間 STW 停頓,這是本次優(yōu)化中最顯著的進步。然而,由于索引構(gòu)建會產(chǎn)生瞬時巨大的內(nèi)存分配需求,超出了 ZGC 即時回收的吞吐能力,從而引發(fā)了短暫的 Allocation Stall(分配停滯)。這成為了系統(tǒng)在極致性能追求下,剩余的一個微小但可感知的抖動來源。

六、追求極致:實現(xiàn)索引無感切換的終極方案

經(jīng)過一系列 JVM 層面的調(diào)優(yōu),我們將服務(wù)成功率從最初的 95% 提升至 99.5%,成效顯著。

  • MaxTenuringThreshold=0:提升至 95%
  • 升級ZGC :提升至99.5%

然而,對于追求極致穩(wěn)定性的系統(tǒng)而言,剩余的 0.5% 的輕微抖動依然是亟待解決的問題。

究其根本,只要大索引的復(fù)制發(fā)生在服務(wù)接流期間,就存在引發(fā)延遲尖刺的風(fēng)險。最終的解決思路不再是“優(yōu)化復(fù)制過程”,而是“讓復(fù)制在無人感知時發(fā)生”。

1.思路轉(zhuǎn)變:從優(yōu)化GC到規(guī)避GC

既然 JVM 層面始終避免不了 1 次大索引復(fù)制,那能否避其鋒芒,新的方案是:進行服務(wù)斷流,在斷流期間主動觸發(fā)并完成索引的復(fù)制晉升過程。待服務(wù)重新接流時,年輕代中已無大對象,后續(xù)所有 Young GC 都將是毫秒級的快速回收。這樣可以根治GC尖刺

運維平臺提供的灰度斷流發(fā)布模式為此方案提供了基礎(chǔ):每次只發(fā)布一批機器,并在其索引加載和切換期間切斷流量,切換完成后再重新接入流量。

然而,僅依靠斷流發(fā)布并不足夠。因為索引的分配不一定會立即觸發(fā) YGC——只有在 Eden 區(qū)空間不足時才會觸發(fā)。

為了更清晰地說明單純依賴“灰度斷流”發(fā)布策略的局限性,我們以一個具體的環(huán)境配置為例進行分析:假設(shè) Eden 區(qū)大小為 3GB待加載的新索引約為 1GB。索引切換前 Eden 區(qū)的初始占用情況,將直接決定發(fā)布時是否會遇到問題。

Case 1:初始占用低,隱患潛伏

索引切換前,Eden 區(qū)僅占用了 1GB,剩余空間充足。

加載 1GB 新索引后,Eden 區(qū)總占用上升至 2GB,仍未達(dá)到 3GB 的容量上限。因此,整個過程不會觸發(fā) Young GC。

當(dāng)服務(wù)重新接流后,業(yè)務(wù)請求產(chǎn)生的對象會迅速占滿 Eden 區(qū)剩余空間,此時 200ms 長暫停勢必導(dǎo)致業(yè)務(wù)請求超時報錯。如下圖:

image-20251011181411847image-20251011181411847

在此場景下,斷流發(fā)布沒有起到任何規(guī)避風(fēng)險的作用,系統(tǒng)抖動依然會發(fā)生。

Case 2:初始占用高,部分緩解

索引切換前,Eden 區(qū)已占用較高空間,例如 2.5GB。

在加載 1GB 新索引的過程中,Eden 區(qū)空間很快被耗盡,從而提前觸發(fā)了一次 Young GC。這次 GC 會將新索引的一部分(例如 500MB)復(fù)制到老年代。

這相當(dāng)于將一次大的復(fù)制操作拆分成兩次較小的操作,一定程度上緩解了后續(xù) GC 的停頓時間。但其效果依然不理想:

image-20251011181552791image-20251011181552791

此場景下,斷流發(fā)布僅能部分緩解問題。復(fù)制操作雖被拆分但未消除,服務(wù)接流后可能仍會有一次較大的停頓。

通過以上分析可見,單純依賴斷流發(fā)布,其效果具有極大的偶然性,嚴(yán)重依賴于發(fā)布時 Eden 區(qū)的初始狀態(tài)。我們將“緩解程度”定義為在斷流期間能提前晉升到老年代的索引比例,那么它與 Eden 區(qū)初始占用的關(guān)系如下圖所示:

image-20251011181750127image-20251011181750127

如圖所示,只有當(dāng)一個批次的 Eden 初始占用較高(落入右側(cè)紅色區(qū)域)時,該批次的發(fā)布才能獲得部分緩解。這意味著整個發(fā)布過程的效果是不均勻、不可控的。要實現(xiàn)徹底的、確定的“無感切換”,必須引入更主動的干預(yù)手段。

2.終極方案:斷流發(fā)布 + 主動預(yù)熱

前述分析表明,依賴系統(tǒng)自然狀態(tài)是不可靠的。要實現(xiàn)確定的“無感切換”,必須采取主動干預(yù)策略。我們的核心思路是:在可控的斷流窗口期內(nèi),主動制造一次“可控的危機”,強制觸發(fā)一次 Young GC,確保新索引100%在此刻被復(fù)制到老年代。

類似“預(yù)熱”的思路,每次新索引切換后、重新接流前,主動、快速地在 Eden 區(qū)創(chuàng)建大量臨時的、短命的“預(yù)熱對象”,瞬間耗盡 Eden 區(qū)的剩余空間。此舉必然會立即觸發(fā)一次 Young GC。

這次 GC 會帶來兩個確定的結(jié)果:

(1) 回收所有無用的“預(yù)熱對象”。

(2) 將唯一存活的大型對象——新索引,復(fù)制到老年代。

當(dāng)服務(wù)重新接流時,年輕代(Eden 和 Survivor 區(qū))已是“空城”,只剩下即將被快速回收的業(yè)務(wù)小對象,從此再無長停頓之憂。

詳細(xì)流程如下圖所示:

image-20251011182241975image-20251011182241975

image-20251011182302673image-20251011182302673

該方案的優(yōu)勢在于,其核心邏輯僅需在關(guān)鍵的索引切換方法中增加一個簡短的循環(huán)即可實現(xiàn),無需復(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 都會很快
    } catch (Exception e) {
        return false;
    }
}

實現(xiàn)注意:預(yù)熱對象的大小需精心設(shè)計。單個對象應(yīng)顯著大于通常的業(yè)務(wù)對象,以快速消耗內(nèi)存,但又必須小于 G1HeapRegionSize的一半(通常為 1MB),以避免被 G1GC 直接當(dāng)作大對象(Humongous Object)分配至老年代,從而繞過年輕代,使預(yù)熱機制失效。

3.效果驗證

部署并啟用“主動預(yù)熱”方案后,我們通過以下三個維度對優(yōu)化效果進行了全面驗證,結(jié)果令人振奮。

1)GC 日志分析:復(fù)制操作被成功前置

對比優(yōu)化前后的 GC 日志,變化一目了然。下圖所示的優(yōu)化后日志清晰記錄了斷流期間發(fā)生的完整過程:

圖片圖片

關(guān)鍵節(jié)點解讀:

① 索引加載:新索引被創(chuàng)建,占據(jù) Eden 區(qū)大部分空間。

② 主動預(yù)熱觸發(fā) YGC:預(yù)熱代碼循環(huán)創(chuàng)建大量臨時對象,迅速耗盡 Eden 區(qū)剩余空間,迫使 JVM 立即觸發(fā)一次 Young GC。

③ 索引晉升:本次 YGC 將新索引(存活對象)完整地復(fù)制到老年代,同時回收所有臨時預(yù)熱對象。

④ 接流后常態(tài):服務(wù)重新接流后,所有的 Young GC 都只用于回收業(yè)務(wù)請求產(chǎn)生的瞬時小對象,耗時均下降至毫秒級,變得快速而平穩(wěn)。

2)系統(tǒng)監(jiān)控:消失的抖動曲線

監(jiān)控系統(tǒng)是最直觀的成效證明。下圖展示了優(yōu)化后一段時間內(nèi)的服務(wù)響應(yīng)時間(RT)監(jiān)控曲線,紅色箭頭標(biāo)注了數(shù)次索引切換事件的發(fā)生時刻。

可以觀察到,在索引切換時,RT 曲線依然平整,幾乎沒有出現(xiàn)任何毛刺或尖峰。這表明索引切換帶來的延遲影響已經(jīng)被完全控制在斷流期內(nèi),對線上業(yè)務(wù)流量做到了真正的“無感”。

3)業(yè)務(wù)指標(biāo):達(dá)到極致穩(wěn)定

最終,一切優(yōu)化都體現(xiàn)在業(yè)務(wù)結(jié)果上。如下圖所示,經(jīng)過本輪徹底優(yōu)化,服務(wù)的日常成功率穩(wěn)定在 99.995% 以上,剩余極少數(shù)的失敗通常源于網(wǎng)絡(luò)抖動等外部因素,GC 引發(fā)的服務(wù)抖動問題已被完全根治。

歷經(jīng)從 JVM 參數(shù)調(diào)優(yōu)到垃圾收集器升級,最終到“發(fā)布策略+主動預(yù)熱”的架構(gòu)與流程優(yōu)化,我們成功地將一個因固有業(yè)務(wù)模式(定期加載大索引)而引發(fā)的性能瓶頸徹底化解,實現(xiàn)了技術(shù)上的極致追求。

七、總結(jié)

本文針對一個高并發(fā)(QPS 10W+)、低延遲(要求毫秒級響應(yīng))、高內(nèi)存壓力(每15分鐘需切換GB級索引)的服務(wù)系統(tǒng),因其頻繁的索引切換導(dǎo)致的GC尖刺和系統(tǒng)抖動問題,進行了一系列從JVM層到架構(gòu)層的深度優(yōu)化實踐。

我們系統(tǒng)地探索了多種解決方案,其效果對比如下:

優(yōu)化手段

服務(wù)可用率(抖動時)

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%

至此,未來無論系統(tǒng) QPS 漲到多高、索引體積膨脹到多大、索引切換多么頻繁,系統(tǒng)都能無感切換索引,穩(wěn)定性不再受到任何影響。

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

2012-08-16 10:43:10

GC

2023-12-04 10:36:46

SessionCookie

2024-05-24 10:36:27

2024-05-21 09:08:57

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

2023-10-04 19:43:38

2023-12-07 12:21:04

GCJVM垃圾

2019-11-27 14:41:50

Java技術(shù)語言

2017-06-09 08:49:07

加載器Full GCJVM

2024-02-26 11:03:05

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

2021-09-29 09:24:21

GCGo STW

2025-06-03 07:00:00

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

2017-06-27 08:41:04

JVM設(shè)計缺陷GC

2017-04-19 12:09:56

數(shù)組動態(tài)擴容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ù)組動態(tài)擴容

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)存回收
點贊
收藏

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

日韩亚洲国产中文字幕欧美| 欧美韩国一区二区| 97久久国产精品| 亚洲一级中文字幕| 色综合一区二区日本韩国亚洲| 亚洲卡通动漫在线| 裸模一区二区三区免费| 亚洲一二区视频| 亚洲人体偷拍| 日韩中文在线视频| youjizz.com日本| 美女写真久久影院| 一区二区三区不卡视频| 日本一区高清在线视频| 狠狠综合久久av一区二区| 三级欧美在线一区| 欧美老少做受xxxx高潮| 免费看污片的网站| 精品亚洲自拍| 欧美高清视频在线高清观看mv色露露十八 | 99久久久国产| 日韩欧美精品中文字幕| 红桃一区二区三区| porn视频在线观看| av网站免费线看精品| 亚洲综合中文字幕在线| 国产天堂第一区| 夜夜嗨一区二区| 欧美成人免费播放| 欧美日韩中文字幕视频| 欧美性生活一级片| 欧美v亚洲v综合ⅴ国产v| 色婷婷一区二区三区av免费看| 中老年在线免费视频| 亚洲午夜精品在线| 韩国黄色一级大片| 久久精品视频免费看| 国产日韩欧美精品在线| 蜜桃日韩视频| 色中色在线视频| 国产经典欧美精品| 91免费国产网站| 美女黄页在线观看| 免费人成精品欧美精品| 国产精品久久9| 国产精品久久久久久久久夜色| 99日韩精品| 久久久久久综合网天天| 久草国产在线观看| 国语精品一区| 欧美精品999| 免费无遮挡无码永久在线观看视频 | 久久99国产精品免费| 国产精品入口尤物| 国产精品51麻豆cm传媒 | 久久人人爽人人片| 精品视频在线观看网站| 日韩一区二区精品| 国产吃瓜黑料一区二区| 国产在线播放精品| 欧美不卡在线视频| 日本一级大毛片a一| av动漫精品一区二区| 日韩av一区二区在线| 亚洲一区二区三区四区五区六区 | 亚洲人成77777| 色哟哟免费在线观看| 国产精品福利一区二区| 天天操天天干天天玩| 亚洲wwwww| 亚洲国产精品影院| 九一国产精品视频| 日本综合字幕| 欧美日韩国产高清一区二区| 久国产精品视频| 亚洲精品一区在线| 日韩国产在线看| 舐め犯し波多野结衣在线观看| 久久精品国产www456c0m| 色诱女教师一区二区三区| 熟女av一区二区| 狠狠干综合网| 日韩av免费一区| 91麻豆成人精品国产| 高清视频一区二区| 欧美国产综合视频| 免费黄色电影在线观看| 午夜久久电影网| 狠狠操精品视频| 精品久久久久久久久久岛国gif| 亚洲精品在线电影| 波多野结衣一二三四区| 综合国产精品| 日本久久久久亚洲中字幕| 国产欧美日韩综合精品一区二区三区 | 亚洲成人精品一区二区| 男人插女人下面免费视频| 久久免费福利| 亚洲欧美中文字幕在线一区| av最新在线观看| 一本色道久久综合亚洲精品高清 | 亚洲黄网站在线观看| 男人揉女人奶房视频60分| 丰满少妇一区| 亚洲老板91色精品久久| 亚洲一级生活片| 久久激情久久| 国产乱子伦精品| 国产原创在线观看| 欧美日韩亚洲天堂| 日本女人黄色片| 欧美特黄一级大片| 26uuu久久噜噜噜噜| 国产视频在线观看免费 | 欧美丰满熟妇bbbbbb| 水蜜桃久久夜色精品一区的特点| 成人性色av| 日本美女在线中文版| 色丁香久综合在线久综合在线观看| 天天色天天干天天色| 日韩伦理视频| 国产第一区电影| 五月婷婷在线观看视频| 亚洲乱码精品一二三四区日韩在线| 国产日产欧美一区二区| 成人福利一区二区| 亚洲免费视频在线观看| 日韩乱码在线观看| 高清在线不卡av| 特大黑人娇小亚洲女mp4| 韩国精品视频在线观看| 国产亚洲欧洲黄色| 精品人妻一区二区色欲产成人| 成人午夜视频在线| 中文字幕の友人北条麻妃| 国产成人免费精品| 中文国产亚洲喷潮| 欧美高清69hd| 国产精品水嫩水嫩| 91网址在线播放| 精品国产99| 国产精品h片在线播放| 十九岁完整版在线观看好看云免费| 亚洲国产综合视频在线观看| 四虎永久免费观看| 亚洲经典在线| 国精产品99永久一区一区| 91美女主播在线视频| 精品美女一区二区三区| 国产精品久久久精品四季影院| 韩日欧美一区二区三区| 成年人免费观看的视频| 97精品资源在线观看| 日韩成人av一区| 久久青青草视频| 久久综合狠狠综合久久综合88| aa在线免费观看| 综合干狼人综合首页| 国产精品白嫩初高中害羞小美女| 高清av在线| 欧美日韩夫妻久久| 曰本女人与公拘交酡| 成人av先锋影音| 虎白女粉嫩尤物福利视频| 精品一区二区三区在线| 国产精品久久久久久久久影视| 91社区在线| 91精品国产日韩91久久久久久| 欧美日韩人妻精品一区二区三区 | 一道本在线观看| 美国av一区二区| 日韩专区第三页| 国产精品xxxav免费视频| 欧美中文字幕精品| 午夜免费播放观看在线视频| 日韩视频中午一区| 97超碰人人干| 国产精品久久网站| 国产欧美视频一区| 美女被久久久| 穿情趣内衣被c到高潮视频| 久久久伦理片| 国产精品狼人色视频一区| 在线heyzo| 亚洲男人天堂九九视频| 91女人18毛片水多国产| 亚洲午夜电影在线观看| 欧洲美一区二区三区亚洲| 极品美女销魂一区二区三区| 岛国大片在线播放| 日韩大片在线观看| 国产美女精品久久久| 日韩av一级| 久久久久久av| 三级外国片在线观看视频| 亚洲成人教育av| 自拍偷拍色综合| 亚洲成人av免费| 精品一区二区三孕妇视频| 成人动漫在线一区| 邪恶网站在线观看| 亚洲精品社区| 粉嫩av一区二区三区天美传媒| 无码少妇一区二区三区| 91人成网站www| 婷婷六月国产精品久久不卡| 欧美理论片在线观看| 91大神xh98hx在线播放| 亚洲精品国产免费| 午夜久久久久久久久久| 欧美少妇性性性| 久久久久久久久久久影院| 亚洲黄色录像片| 91无套直看片红桃在线观看| 91丨porny丨户外露出| 日本亚洲一区二区三区| 欧美aaa在线| 99爱视频在线| 亚洲视频高清| 69精品丰满人妻无码视频a片| av中字幕久久| 免费成人av网站| 麻豆精品99| 福利视频一区二区三区| 成人污版视频| 国产欧美日韩最新| 在线成人视屏| 国产mv久久久| 亚洲伦乱视频| 秋霞av国产精品一区| а√天堂资源官网在线资源| 欧美日韩ab片| 伊人影院蕉久影院在线播放| www.日韩系列| 免费在线看a| 久久精品视频在线观看| av免费观看一区二区| 亚洲人成网在线播放| 午夜在线观看视频18| 亚洲高清在线观看| 视频一区 中文字幕| 亚洲国产精品久久久久| 亚洲精品一区二区三区四区| 日韩欧美成人一区| 性做久久久久久久久久| 日韩精品一区二区三区老鸭窝| www.97超碰| 日韩欧美国产电影| 亚洲成熟女性毛茸茸| 日韩你懂的在线观看| 亚洲男女视频在线观看| 欧美r级电影在线观看| 欧美一级淫片aaaaaa| 日韩精品有码在线观看| 色视频在线观看免费| 亚洲欧美另类在线观看| 91在线看片| 久久久www成人免费精品| 黄色成人在线观看| 欧美—级高清免费播放| 欧美aa在线| 国产精品国产自产拍高清av水多| 欧美电影在线观看网站| 亚洲一区二区三区四区在线播放| 综合久久成人| 欧美精品七区| 日韩理论在线| 久久精品xxx| 性色一区二区三区| 中文字幕永久视频| 国产乱人伦偷精品视频免下载 | 99精品人妻国产毛片| 欧美少妇xxx| 亚洲第一第二区| 亚洲欧美精品一区| 黄网站在线免费| 久久久噜久噜久久综合| 国产伦精品一区二区三区视频金莲| 国产精品电影在线观看| 视频在线一区| 日本不卡一区| 一区二区三区四区日韩| 各处沟厕大尺度偷拍女厕嘘嘘| 美女任你摸久久| 日本wwwwwww| 中文字幕二三区不卡| 精品无码黑人又粗又大又长| 色婷婷激情综合| a天堂视频在线| 亚洲欧美精品伊人久久| 在线电影福利片| 欧美在线观看日本一区| 精品国产亚洲一区二区三区大结局 | 国产成人a人亚洲精品无码| 日韩精品免费在线观看| 麻豆av在线免费看| 欧美亚洲国产另类| 国产一区二区三区黄网站| 免费成人av网站| 欧美视频成人| 四季av一区二区三区| 97se亚洲国产综合自在线| www.超碰在线观看| 欧美在线免费观看亚洲| 日韩一级片免费| 久久综合伊人77777蜜臀| 日韩影片中文字幕| 官网99热精品| 亚洲精品一区二区妖精| 黄色一级大片在线观看| 成人免费观看av| 中文字幕五月天| 欧美日韩你懂得| 国产黄色片在线播放| 4438全国亚洲精品在线观看视频| 欧美1区2区3| 亚洲欧美日本国产有色| 久久三级视频| 800av在线播放| 亚洲午夜免费视频| 国产黄色免费大片| 久久精品视频在线观看| 久久人人视频| 日韩国产美国| 水蜜桃久久夜色精品一区的特点| 日韩网站在线播放| 图片区小说区区亚洲影院| 成人激情四射网| 久久99国产精品自在自在app| 成人在线免费电影网站| 日韩影视精品| 日av在线不卡| 亚洲AV无码成人精品区明星换面| 欧美日韩亚洲一区二| 亚洲欧洲综合在线| 国产91ⅴ在线精品免费观看| 青青操综合网| 国产视频一视频二| 337p粉嫩大胆噜噜噜噜噜91av| 日韩经典在线观看| 日韩精品极品视频免费观看| 都市激情国产精品| 久久久人人爽| 视频一区视频二区在线观看| 能免费看av的网站| 欧美在线|欧美| 波多野结衣在线影院| 国产精品丝袜视频| 91亚洲国产高清| 国内av免费观看| 亚洲午夜三级在线| 国产又爽又黄网站亚洲视频123| 91国内精品久久| 亚洲精品无吗| www.涩涩涩| 18成人在线观看| 国模人体一区二区| 97在线观看免费| 亚洲小说图片视频| 婷婷六月天在线| 亚洲美女少妇撒尿| 内射无码专区久久亚洲| 欧美一级片一区| 青青草成人影院| 少妇性l交大片7724com| 亚洲成av人综合在线观看| 欧美老女人性开放| 91精品国产自产在线观看永久| 亚洲精品888| 欧美一区二区免费在线观看| 色综合欧美在线| 美女隐私在线观看| 国产成人一区二区三区免费看| 国产精品日韩久久久| 国产又粗又长又硬| 日韩精品一区二区三区四区 | 777xxx欧美| heyzo高清在线| 亚洲成人蜜桃| 成人看片黄a免费看在线| 日韩综合在线观看| 久久在线精品视频| 台湾佬综合网| 亚洲网中文字幕| 婷婷综合在线观看| 麻豆网站在线看| 久久亚洲午夜电影| 精久久久久久久久久久| 精品国产乱码一区二区| 色吧影院999| 欧美在线导航| 午夜天堂在线视频| 精品国产精品自拍| www视频在线免费观看| 久久青青草原| 国产一区二区三区免费在线观看| 久久精品视频5| 久久免费视频观看| 成人羞羞视频在线看网址| 中文字幕人妻熟女在线|