精品欧美一区二区三区在线观看 _久久久久国色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ù)棧公眾號

日韩女优在线视频| 九九热久久66| 无码人妻精品中文字幕| 91成人在线网站| 亚洲精品国产精品乱码不99| 99re热精品| 国产精品6666| 色综合中文网| 欧美人体做爰大胆视频| 国产成人三级视频| 天天av天天翘| 日产欧产美韩系列久久99| 日韩一区二区精品视频| 久久发布国产伦子伦精品| 啊啊啊久久久| 日本一区二区视频在线| 91在线精品播放| 国语对白一区二区| blacked蜜桃精品一区| 5858s免费视频成人| 精品无码一区二区三区爱欲| 福利片在线看| 国产99久久久国产精品潘金网站| 91av视频在线| 精品亚洲乱码一区二区| 好吊妞国产欧美日韩免费观看网站| 欧美日韩国产综合新一区 | 免费高清视频精品| 色与欲影视天天看综合网| 3d动漫精品啪啪一区二区下载| 日韩欧美三区| 欧美日韩国产一区二区| 亚洲精品一区二区三区樱花| 欧美熟妇交换久久久久久分类 | 91蜜桃网址入口| 国产日韩欧美视频在线| 五月婷婷亚洲综合| 永久亚洲成a人片777777| 亚洲欧美日韩成人| 少妇熟女视频一区二区三区| 欧美色网在线| 亚洲成人精品一区二区| 四虎免费在线观看视频| 国产精品一级伦理| 99久久精品久久久久久清纯| 亚洲自拍偷拍色片视频| 午夜视频网站在线观看| 中文日韩在线| 欧美激情中文字幕在线| 911国产在线| 教室别恋欧美无删减版| 亚洲缚视频在线观看| 欧美色图校园春色| 色成人综合网| 欧美色网一区二区| 日韩视频第二页| av小说在线播放| 亚洲乱码国产乱码精品精98午夜 | 福利一区二区免费视频| 欧美日韩在线一区| 精品久久久久久无码中文野结衣| 欧美三级理伦电影| 中文字幕欧美国产| 欧美日韩在线播放一区二区| 先锋av资源站| eeuss国产一区二区三区| 亚洲mm色国产网站| 国产三级伦理片| 精品一区二区三区av| 国产精品视频成人| 中文字幕人妻一区二区在线视频| 久久伊人亚洲| 国产精品18久久久久久首页狼 | 一本色道久久亚洲综合精品蜜桃| 日韩在线电影一区| 一区二区三区免费在线视频| 日韩精品欧美精品| 日韩av大片免费看| 国产免费a视频| 日本色综合中文字幕| 国产精品福利无圣光在线一区| 在线视频一区二区三区四区| 久久综合亚州| 国产欧美一二三区| 视频三区二区一区| 午夜在线视频播放| 国产精品国产三级国产| 中文字幕第一页亚洲| 综合图区亚洲| 亚洲第一狼人社区| 国产成人a亚洲精v品无码| 久久毛片亚洲| 欧美午夜电影一区| 中文字幕线观看| 精品国模一区二区三区欧美| 精品久久久久久综合日本欧美| 成人在线电影网站| 久久成人av| 日韩中文在线中文网在线观看| av最新在线观看| 国产精品99免费看| 97av在线视频| 中文字幕+乱码+中文字幕明步| 精品写真视频在线观看| 国产98在线|日韩| 艳母动漫在线看| 国产精品毛片高清在线完整版| 少妇高潮大叫好爽喷水| 黄色漫画在线免费看| 欧美专区亚洲专区| 中文字幕无码毛片免费看| 久久精品福利| 少妇高潮 亚洲精品| 麻豆一区二区三区精品视频| 麻豆精品网站| 亚洲影院高清在线| 激情小说 在线视频| 一区二区在线观看免费视频播放| 各处沟厕大尺度偷拍女厕嘘嘘| 国产原创一区| 亚洲国产精品高清久久久| 国产123在线| 欧美日韩国产免费观看| 国内揄拍国内精品少妇国语| 五月激情丁香网| 国产a精品视频| 亚洲欧美日韩精品在线| 免费成人在线电影| 欧美一级片免费看| 国产91丝袜美女在线播放| 国产一区日韩一区| 成人黄色av网| 九色在线观看| 香蕉成人伊视频在线观看| 91小视频在线播放| av一区二区在线播放| 91国在线精品国内播放| 国产草草影院ccyycom| 日本一区二区三区免费乱视频| 国产真实老熟女无套内射| 欧美高清xxx| 亚洲视频axxx| 久久久国产高清| 国产91综合网| 青青草原网站在线观看| 成人激情视屏| 亚洲欧美国产精品| 欧美一级视频免费观看| 国产激情一区二区三区四区 | 99国产精品一区| 四虎4hu永久免费入口| www.一区| 亚洲色图15p| 国产一区二区99| 成人黄色777网| 91传媒免费视频| 白嫩亚洲一区二区三区| 最新国产精品亚洲| 成人黄色三级视频| 国产日韩欧美不卡| 国产第一页视频| 亚洲黄色录像| 国产91在线高潮白浆在线观看| 人妻精品一区一区三区蜜桃91| 夜色激情一区二区| 亚洲午夜精品在线观看| 欧美成人嫩草网站| 91网免费观看| 日本无删减在线| 亚洲成色777777女色窝| 福利一区二区三区四区| 99精品视频在线观看| 成人毛片一区二区| 亚洲国产欧美日韩在线观看第一区 | 最新天堂在线视频| 四季av一区二区三区免费观看| 国产精品欧美激情| 蜜桃视频网站在线观看| 91精品国产综合久久久久久久| 国产一二三四区| 国产成人在线观看免费网站| 成人免费性视频| 久久久伦理片| 日韩av色综合| 三级外国片在线观看视频| 欧美日韩国产一级二级| 国产免费久久久久| 大胆亚洲人体视频| 成年人观看网站| 日本大胆欧美| 999视频在线免费观看| www欧美xxxx| 亚洲老司机av| 一区不卡在线观看| 亚洲黄色在线视频| 亚洲一区二区三区综合| 日本在线不卡视频| 成人在线免费高清视频| 亚欧日韩另类中文欧美| 国产精品视频免费在线观看| 影音先锋在线播放| 日韩大片免费观看视频播放| 在线免费一区二区| 亚洲精品乱码久久久久久黑人| 国产精品无码一区二区三| 丝袜美腿亚洲综合| 天天想你在线观看完整版电影免费| 好吊妞视频这里有精品| 国产精品视频免费在线| 超碰在线资源| 中文字幕久久精品| 色噜噜一区二区三区| 欧美性videosxxxxx| 久久网一区二区| 亚洲国产精品精华液ab| 日韩黄色一区二区| 蜜桃av一区二区三区电影| av在线观看地址| 99久精品视频在线观看视频| 国产一区二区免费在线观看| 成人精品动漫| 91成人在线视频| 国产1区在线| 亚洲日韩中文字幕在线播放| 丰满人妻一区二区| 欧美嫩在线观看| 国语对白永久免费| 亚洲伊人伊色伊影伊综合网| 国产熟女一区二区| 不卡一区在线观看| 在线成人免费av| 青青草91视频| 成年人视频观看| 欧美日韩国产高清| 在线视频91| 欧美精品乱码| 久久国产精品精品国产色婷婷| 欧美a级大片在线| 国产精品欧美日韩久久| 午夜久久中文| 欧美激情精品久久久久久大尺度 | 天堂中文а√在线| 亚洲欧美制服综合另类| 欧美一级特黄aaaaaa大片在线观看| 欧美性大战久久久久久久| 人妻丰满熟妇av无码区| 午夜一区二区三区在线观看| 免费视频一二三区| 亚洲男女一区二区三区| 永久免费看片视频教学| 国产精品女同互慰在线看| 精品人伦一区二区| 久久综合九色综合97_久久久| 日批视频免费看| 国产九九视频一区二区三区| 男人添女人下面免费视频| 日韩高清不卡在线| 免费观看成人网| 午夜一级在线看亚洲| 日本www在线视频| 亚洲人成高清| 久久久亚洲精品无码| 在线播放日韩| av在线观看地址| 一区在线视频观看| 欧美精品久久久久久久久久久| 欧美久久久久| 国产精品久久久久久久久电影网| 欧美成人一品| 日本香蕉视频在线观看| 精品动漫3d一区二区三区免费| 国产aaa免费视频| 一区二区亚洲| 18禁男女爽爽爽午夜网站免费| 美女视频一区免费观看| 欧美在线观看视频网站| 日日夜夜一区二区| 男女视频在线看| 久久精品免费看| 五月天丁香花婷婷| 国产成人午夜视频| 182在线视频| 久久精品欧美一区二区三区不卡 | 成人性生交大片免费观看网站| 国产成人精彩在线视频九色| 91伊人久久| 91日韩在线播放| 91精品国产自产在线丝袜啪 | 97超碰资源站| 日韩免费视频一区二区| 天天综合网天天综合| 国产偷国产偷亚洲清高网站| 成人jjav| 成人97在线观看视频| 国产在线拍揄自揄拍视频 | 精品一区在线观看视频| 亚洲国产美女搞黄色| 中文字幕视频网| 欧美日韩一区不卡| 亚洲精品久久久久久动漫器材一区| 亚洲精品美女视频| 日本最新在线视频| 性欧美在线看片a免费观看| 免费福利视频一区二区三区| 成人国产精品av| 国产精品欧美大片| 丝袜足脚交91精品| 欧美视频久久| 超碰影院在线观看| 国产精品一级黄| 国产又爽又黄无码无遮挡在线观看| 国产精品午夜春色av| 久视频在线观看| 欧美视频自拍偷拍| 欧美天堂在线视频| 最近2019免费中文字幕视频三| 欧美hdxxx| 国产精品自产拍在线观看中文| 盗摄牛牛av影视一区二区| 日韩国产美国| 影音先锋在线一区| 四季av一区二区三区| 91碰在线视频| 欧美交换国产一区内射| 欧美中文一区二区三区| 日韩一级在线播放| 久久精品视频播放| 唐人社导航福利精品| 国产一区二区久久久| 中文一区一区三区免费在线观看| 成年人视频在线免费| 成人va在线观看| 国产午夜精品理论片| 色琪琪一区二区三区亚洲区| 亚洲精品国产手机| xxx一区二区| 91超碰碰碰碰久久久久久综合| 精品欧美日韩在线| 国内一区二区三区| 久久久久久久久久毛片| 中文字幕乱码亚洲精品一区| 欧美激情亚洲综合| 精品欧美一区二区在线观看| 91一区二区三区在线| 91精品国产综合久久香蕉922| 国产午夜一区| 久久久久狠狠高潮亚洲精品| 99久久免费国产| 97精品国产91久久久久久| 欧美中文字幕精在线不卡| 国产精品乱码| 欧美99久久| 久久久九九九热| 国产精品理伦片| 久草视频在线免费| 亚洲欧美日韩一区二区三区在线| 91福利在线免费| 99久久自偷自偷国产精品不卡| 91精品啪在线观看国产81旧版| 亚洲黄色av片| 国产精品久久久久一区二区三区| 成人黄色三级视频| 在线观看国产精品日韩av| 黄瓜视频成人app免费| 欧美日产一区二区三区在线观看| 国产欧美三级| 国产精品久久不卡| 天天色综合天天| 熟妇高潮一区二区高潮| 久久免费视频网| 久久久久久毛片免费看 | 日韩精品丝袜在线| 欧美xxxhd| 欧美裸体网站| 久久国产精品毛片| 欧美激情 一区| 亚洲综合精品伊人久久| 神马精品久久| 国模gogo一区二区大胆私拍| 成人春色在线观看免费网站| 国产曰肥老太婆无遮挡| 成人av在线资源网| 国产69精品久久久久久久久久| 欧美成人伊人久久综合网| 欧美大片黄色| 国产在线欧美日韩| 首页国产欧美久久| 老熟妻内射精品一区| 亚洲精品一区在线观看| 在线天堂资源| 亚洲va久久久噜噜噜久久狠狠| 韩国午夜理伦三级不卡影院| 精品少妇久久久久久888优播| 精品亚洲一区二区三区在线观看| 日韩pacopacomama| 国产麻豆电影在线观看| 99视频一区二区| 中文精品久久久久人妻不卡| 麻豆国产va免费精品高清在线| 免费成人三级|