在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

JVM的垃圾機(jī)制是如何工作的呢?

OSC開(kāi)源社區(qū) ? 來(lái)源:OSCHINA 社區(qū) ? 2023-02-28 16:08 ? 次閱讀

前言

本文所有介紹僅限于HotSpot虛擬機(jī),
本文先介紹了垃圾回收的必要手段,基于這些手段講解了歷代垃圾回收算法是如何工作的,
每一種算法不會(huì)講的特別詳細(xì),只為讀者從算法角度理解工作原理,從而引出ZGC,方便讀者循序漸進(jìn)地了解。
GC 是 Garbage Collection 的縮寫,顧名思義垃圾回收機(jī)制,即當(dāng)需要分配的內(nèi)存空間不再使用的時(shí)候,JVM 將調(diào)用垃圾回收機(jī)制來(lái)回收內(nèi)存空間。

那么 JVM 的垃圾機(jī)制是如何工作的呢? 第一步識(shí)別出哪些空間不再使用(識(shí)別并標(biāo)記出哪些對(duì)象已死); 第二步回收不再使用空間(清除已死對(duì)象 )

判斷對(duì)象是否已死

判斷對(duì)象是否已死通常有兩種方式,引用計(jì)數(shù)法和可達(dá)性分析法

引用計(jì)數(shù)法

給對(duì)象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值就加 1: 當(dāng)引用失效時(shí),計(jì)數(shù)器值就減 1; 任何時(shí)刻計(jì)數(shù)器為 0 的對(duì)象就是不能再被使用的。 簡(jiǎn)單高效,但無(wú)法解決循環(huán)引用問(wèn)題,a=b,b=a 引用計(jì)數(shù)法并沒(méi)有在產(chǎn)品級(jí)的 JVM 中得到應(yīng)用

可達(dá)性分析法

這個(gè)算法的基本思路就是通過(guò)一系列的稱為 “GC Roots” 的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開(kāi)始向下搜索,搜索所走過(guò)的路徑稱為引用鏈 ( Reference Chain), 當(dāng)一個(gè)對(duì)象到 GC Roots 沒(méi)有任何引用鏈相連 (用圖論的話來(lái)說(shuō),就是從 GC Roots 到這個(gè)對(duì)象不可達(dá)) 時(shí),則證明此對(duì)象是不可用的。

?cde0f95a-aa86-11ed-bfe3-dac502259ad0.png

不過(guò)可達(dá)性算法中的對(duì)象并不是立即死亡的,對(duì)象擁有一次自我拯救的機(jī)會(huì),對(duì)象被系統(tǒng)宣告死亡至少要經(jīng)歷兩次標(biāo)記過(guò)程,第一次是經(jīng)過(guò)可達(dá)性分析之后沒(méi)有與 GC Roots 相連的引用鏈,第二次是在由虛擬機(jī)自動(dòng)建立的Finalize隊(duì)列中判斷是否需要執(zhí)行 finalize () 方法。 HotSopt 虛擬機(jī)采用該算法。

清除已死對(duì)象的方式

標(biāo)記清除算法

先標(biāo)記再清除 不足:1 效率問(wèn)題,標(biāo)記和清除效率都不高。2 空間問(wèn)題,產(chǎn)生大量空間碎片

復(fù)制算法

內(nèi)存分兩塊,A,B A 用完了,將存活對(duì)象拷貝到 B,A 清理掉 代價(jià):內(nèi)存少了一半。 HotSopt 虛擬機(jī)用此算法回收新生代。將新生代內(nèi)存劃分為 8:1:1 的 Eden 和 Survivor 解決復(fù)制算法內(nèi)存使用率低的問(wèn)題 ?

ce0a7ac8-aa86-11ed-bfe3-dac502259ad0.png??

標(biāo)記整理算法

老年代使用,方式和標(biāo)記清除類似,只是不直接清除,而是將后續(xù)對(duì)象向一端移動(dòng),并清理掉邊界以外的內(nèi)存。 ?

ce2094ca-aa86-11ed-bfe3-dac502259ad0.png

分代收集算法

分代收集是一個(gè)算法方案,整合了以上算法的優(yōu)點(diǎn),一般是把 Java 堆分為新生代和老年代,在新生代中,使用復(fù)制算法老年代 “標(biāo)記一清理” 或者 “標(biāo)記一整理”

歷代垃圾收集器簡(jiǎn)介

通過(guò)上文我們了解了怎樣識(shí)別垃圾,怎樣清理垃圾,接下來(lái),講 ZGC 之前,我們回顧一下歷代垃圾回收是怎樣做的,主要是想給讀者一種歷史的視角,任何技術(shù)都不是憑空產(chǎn)生的,更多的是在前人成果之上進(jìn)行優(yōu)化整合 我們先看一個(gè)歷代 JDK 垃圾收集器對(duì)比表格,以下表格著重說(shuō)明或引出幾個(gè)問(wèn)題: 1 CMS 從來(lái)未被當(dāng)作默認(rèn) GC,且已廢棄 2 CMS 的思想其實(shí)部分被 ZGC 吸收,CMS 已死,但他的魂還在 3 JDK11、JDK17 為長(zhǎng)期迭代版本,項(xiàng)目中應(yīng)優(yōu)先使用這兩個(gè)版本

版本 發(fā)布時(shí)間 默認(rèn)收集器 事件
jdk1.3 2000-05-08 serial
jdk1.4 2004-02-06 ParNew
jdk1.5/5.0 2004-09-30 Parallel Scavenge/serial CMS 登場(chǎng)
jdk1.6/6.0 2006-12-11 Parallel Scavenge/Parallel Old
dk1.7/7.0 2011-07-28 Parallel Scavenge/Parallel Old G1 登場(chǎng)
jdk1.8/8.0 2014-03-18 Parallel Scavenge/Parallel Old
jdk1.9/9.0 2014-09-8 G1 CMS 廢棄
jdk10 2018-03-21 G1
jdk11 2018-09-25 G1 ZGC 登場(chǎng)
jdk12 2019-3 G1 Shenandoah
jdk13 2019-9 G1
jdk14 2020-3 G1 CMS 移除
jdk15 2020-9-15 G1 ZGC、Shenandoah 轉(zhuǎn)正
jdk16 2021-3-16 G1
jdk17 2021-09-14 G1 ZGC 分代
jdk18 2022-3-22 G1
jdk19 2022-9-22 G1

GC 分類

我們經(jīng)常在各種場(chǎng)景聽(tīng)到以下幾種 GC 名詞,Young GC、Old GC、Mixed GC、Full GC、Major GC、Minor GC,他們到底什么意思,本人進(jìn)行了以下梳理 首先 GC 分兩類,Partial GC(部分回收),F(xiàn)ull GC Partial GC:并不收集整個(gè) GC 堆的模式,以下全是 Partial GC 的子集 Young GC:只收集 young gen 的 GC Old GC:只收集 old gen 的 GC。

只有 CMS 的 concurrent collection 是這個(gè)模式 Mixed GC:只有 G1 有這個(gè)模式,收集整個(gè) young gen 以及部分 old gen 的 GC。

Minor GC:只有 G1 有這個(gè)模式,收集整個(gè) young gen Full GC:收集整個(gè)堆,包括 young gen、old gen、perm gen(如果存在的話)等所有部分的模式。

Major GC:通常是跟 full GC 是等價(jià)的

serial 收集器

單線程收集器,“單線程” 的意義并不僅僅說(shuō)明它只會(huì)使用一個(gè) CPU 或一條收集線程去完成垃圾收集工作,更重要的是在它進(jìn)行垃圾收集時(shí),必須暫停其他所有的工作線程, 直到它收集結(jié)束。它依然是虛擬機(jī)運(yùn)行在 Client 模式下的默認(rèn)新生代收集器。

它也有著優(yōu)于其他收集器的地方:簡(jiǎn)單而高效 (與其他收集器的單線程比), 對(duì)于限定單個(gè) CPU 的環(huán)境來(lái)說(shuō),Serial I 收集器由于沒(méi)有線程交互的開(kāi)銷,專心做垃圾收集自然可以獲得最高的單線程收集效率。 下圖彩色部分說(shuō)明了它的算法,簡(jiǎn)單粗暴 1 停止用戶線程 2 單線程垃圾回收新生代 3 重啟用戶線程 ??

ce3dc5d6-aa86-11ed-bfe3-dac502259ad0.png

ParNew 收集器

Parnew 收集器其實(shí)就是 Serial l 收集器的多線程版本。它是許多運(yùn)行在 Server 模式下的虛擬機(jī)中首選的新生代收集器,其中有一個(gè)與性能無(wú)關(guān)但很重要的原因是,除了 Serial 收集器外,目前只有它能與 CMS 收集器配合工作。Pardew 收集器在單 CPU 的環(huán)境中絕對(duì)不會(huì)有比 Serial 收集器更好的效果。

它默認(rèn)開(kāi)啟的收集線程數(shù)與 CPU 的數(shù)量相同,在 CPU 非常多 (臂如 32 個(gè)) 的環(huán)境下,可以使用 - XX: ParallelGCThreads 參數(shù)來(lái)限制垃圾收集的線程數(shù)。

ParNew 收集器追求降低 GC 時(shí)用戶線程的停頓時(shí)間,適合交互式應(yīng)用,良好的反應(yīng)速度提升用戶體驗(yàn). 下圖彩色部分說(shuō)明了它的算法,同樣簡(jiǎn)單粗暴 1 停止用戶線程 2 多線程垃圾回收新生代 3 重啟用戶線程

ce5f70b4-aa86-11ed-bfe3-dac502259ad0.png

Parallel Scavenge 收集器

Parallel Scavenge 收集器是一個(gè)新生代收集器,它也是使用復(fù)制算法的收集器,又是并行的多線程收集器。算法的角度它和 ParNew 一樣,在此就不畫圖解釋了 Parallel Scavenge 收集器的目標(biāo)則是達(dá)到一個(gè)可控制的吞吐量( Throughput) 吞吐量是指用戶線程運(yùn)行時(shí)間占 CPU 總時(shí)間的比例 通過(guò)以下兩種方式可達(dá)到目的: 1. 在多 CPU 環(huán)境中使用多條 GC 線程,從而垃圾回收的時(shí)間減少,從而用戶線程停頓的時(shí)間也減少; 2. 實(shí)現(xiàn) GC 線程與用戶線程并發(fā)執(zhí)行。

Serial Old 收集器

Serial Old 是 Serial 收集器的老年代版本,它同樣是一個(gè)單線程收集器,使用 “標(biāo)記整理” 算法。這個(gè)收集器的主要意義也是在于給 Client 模式下的虛擬機(jī)使用。 如果在 Server 模式下,那么它主要還有兩大用途: 一種用途是在 JDK1.5 以及之前的版本中與 ParallelScavenge 收集器搭配使用, 另一種用途就是作為 CMS 收集器的后備預(yù)案,在并發(fā)收集發(fā)生 Concurrent Mode Failure 時(shí)使用 下圖彩色部分說(shuō)明了它的算法,同樣簡(jiǎn)單粗暴 1 停止用戶線程 2 單線程垃圾回收老年代 3 重啟用戶線程

?? ce7bcfc0-aa86-11ed-bfe3-dac502259ad0.png

Parallel Old 收集器

Paralle Old 是 Parallel Scavenge 收集器的老年代版本,一般它們搭配使用,追求 CPU 吞吐量,使用多線程和 “標(biāo)記一整理” 算法。 下圖彩色部分說(shuō)明了它的算法,同樣簡(jiǎn)單粗暴 1 停止用戶線程 2 多線程垃圾回收老年代 3 重啟用戶線程

cea17ea0-aa86-11ed-bfe3-dac502259ad0.png

CMS 收集器

以上 5 種垃圾回收原理不難理解,算法之所以如此簡(jiǎn)單個(gè)人理解在當(dāng)時(shí)使用這種算法就夠了,隨著 JAVA 的攻城略地,有一種垃圾回收需求出現(xiàn),即使用盡量短的回收停頓時(shí)間,以避免過(guò)久的影響用戶線程,CMS 登場(chǎng)了。 CMS (Concurrent Mark Sweep) 收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。

想要達(dá)到目的,就要分析 GC 時(shí)最占用時(shí)間的是什么操作,比較浪費(fèi)時(shí)間的是標(biāo)記已死對(duì)象、清除對(duì)象,那么如果可以和用戶線程并發(fā)的進(jìn)行,GC 的停頓基本就限制在了標(biāo)記所花費(fèi)的時(shí)間。

?ceb78966-aa86-11ed-bfe3-dac502259ad0.png

如上圖,CMS 收集器是基于 “標(biāo)記一清除” 法實(shí)現(xiàn)的,它的運(yùn)作過(guò)程分為 4 個(gè)步驟

?初始標(biāo)記 (EMS initial mark) stop the world

?并發(fā)標(biāo)記 (CMS concurrent mark)

?重新標(biāo)記 (CMS remark) stop the world

?并發(fā)清除 (CMS concurrent sweep)

初始標(biāo)記的作用是查找 GC Roots 集合的過(guò)程,這個(gè)過(guò)程處理對(duì)象相對(duì)較少,速度很快。(為什么要進(jìn)行初始標(biāo)記:枚舉根結(jié)點(diǎn)。并發(fā)標(biāo)記是實(shí)際標(biāo)記所有對(duì)象是否已死的過(guò)程,比較耗時(shí),所以采用并發(fā)的方式。

重新標(biāo)記主要是處理并發(fā)標(biāo)記期間所產(chǎn)生的新的垃圾。

重新標(biāo)記階段不需要再重新標(biāo)記所有對(duì)象,只對(duì)并發(fā)標(biāo)記階段改動(dòng)過(guò)的對(duì)象做標(biāo)記即可。 優(yōu)點(diǎn): 并發(fā)收集、低停頓 缺點(diǎn): CMS 收集器對(duì) CPU 資源非常敏感。

CMS 收集器無(wú)法處理浮動(dòng)垃圾 (Floating Garbage), 可能出現(xiàn) “Concurrent ModeFailure” 失敗而導(dǎo)致另一次 Full GC 的產(chǎn)生。

“標(biāo)記一清除” 法導(dǎo)致大量空間碎片產(chǎn)生,以至于老年代還有大量空間,卻沒(méi)有整塊空間存儲(chǔ)某對(duì)象。

Concurrent ModeFailure可能原因及方案
原因1:CMS觸發(fā)太晚
方案:將-XX:CMSInitiatingOccupancyFraction=N調(diào)小 (達(dá)到百分比進(jìn)行垃圾回收);
原因2:空間碎片太多
方案:開(kāi)啟空間碎片整理,并將空間碎片整理周期設(shè)置在合理范圍;
-XX:+UseCMSCompactAtFullCollection (空間碎片整理)
-XX:CMSFullGCsBeforeCompaction=n
原因3:垃圾產(chǎn)生速度超過(guò)清理速度
晉升閾值過(guò)小;
Survivor空間過(guò)小,導(dǎo)致溢出;
Eden區(qū)過(guò)小,導(dǎo)致晉升速率提高;存在大對(duì)象;

G1 收集器

G1 是一款面向服務(wù)端應(yīng)用的垃圾收集器。下文會(huì)簡(jiǎn)單講解一下它的 “特點(diǎn)” 和 “內(nèi)存分配與回收策略”,有基礎(chǔ)或不感興趣的同學(xué)直接跳到 “G1 垃圾回收流程”

特點(diǎn)

并行與并發(fā) G1 能充分利用多 CPU、多核環(huán)境下的硬件優(yōu)勢(shì),使用多個(gè) CPU (CPU 或者 CPU 核心) 來(lái)縮短 Stop-The- World 停頓的時(shí)間,部分其他收集器原本需要停頓 Java 線程執(zhí)行的 GC 動(dòng)作,G1 收集器仍然可以通過(guò)并發(fā)的方式讓 Java 程序繼續(xù)執(zhí)行。

分代收集 與其他收集器一樣,分代概念在 G1 中依然得以保留。雖然 G1 可以不需要其他收集器配合就能獨(dú)立管理整個(gè) GC 堆,但它能夠采用不同的方式去處理新創(chuàng)建的對(duì)象和已經(jīng)存活了一段時(shí)間、熬過(guò)多次 GC 的舊對(duì)象以獲取更好的收集效果。

空間整合 與 CMS 的 “標(biāo)記一清理” 算法不同,G1 從整體來(lái)看是基于 “標(biāo)記一整理” 算法實(shí)現(xiàn)的收集器,從局部 (兩個(gè) Region 之間) 上來(lái)看是基于 “復(fù)制” 算法實(shí)現(xiàn)的,但無(wú)論如何,這兩種算法都意味著 G1 運(yùn)作期間不會(huì)產(chǎn)生內(nèi)存空間碎片,收集后能提供規(guī)整的可用內(nèi)存。這種特性有利于程序長(zhǎng)時(shí)間運(yùn)行,分配大對(duì)象時(shí)不會(huì)因?yàn)闊o(wú)法找到連續(xù)內(nèi)存空間而提前觸發(fā)下一次 GC。

可預(yù)測(cè)的停頓 這是 G1 相對(duì)于 CMS 的另一大優(yōu)勢(shì),降低停頓時(shí)間是 G1 和 CMS 共同的關(guān)注點(diǎn),但 G1 除了追求低停頓外,還能建立可預(yù)測(cè)的停頓時(shí)間模型,能讓使用者明確指定在一個(gè)長(zhǎng)度為 M 毫秒的時(shí)間片段內(nèi),消耗在垃圾收集上的時(shí)間不得超過(guò) N 毫秒,這幾乎已經(jīng)是實(shí)時(shí) Java (RTSJ) 的垃圾收集器的特征了。

在 G1 之前的其他收集器進(jìn)行收集的范圍都是整個(gè)新生代或者老年代,而 G1 不再是這樣。使用 G1 收集器時(shí),Java 堆的內(nèi)存布局就與其他收集器有很大差別,它將整個(gè) Java 堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域 (Region), 雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離的了,它們都是一部分 Region (不需要連續(xù)) 的集合

內(nèi)存分配與回收策略

對(duì)象優(yōu)先在 Eden 分配 大多數(shù)情況下,對(duì)象在新生代 Eden 區(qū)中分配。當(dāng) Eden 區(qū)沒(méi)有足夠空間進(jìn)行分配時(shí),虛擬機(jī)將發(fā)起一次 Minor[?ma?n?(r)] GC 大對(duì)象直接進(jìn)入老年代 所謂的大對(duì)象是指,需要大量連續(xù)內(nèi)存空間的 Java 對(duì)象,最典型的大對(duì)象就是那種很長(zhǎng)的字符串以及數(shù)組。

大對(duì)象對(duì)虛擬機(jī)的內(nèi)存分配來(lái)說(shuō)就是一個(gè)壞消息 (比遇到一個(gè)大對(duì)象更加壞的消息就是遇到一群 “朝生夕滅” 的 “短命大對(duì)象” 寫程序的時(shí)候應(yīng)當(dāng)避免), 經(jīng)常出現(xiàn)大對(duì)象容易導(dǎo)致內(nèi)存還有不少空間時(shí)就提前觸發(fā)垃圾收集以獲取足夠的連續(xù)空間來(lái) “安置” 它們。

長(zhǎng)期存活的對(duì)象將進(jìn)入老年代 虛擬機(jī)給每個(gè)對(duì)象定義了一個(gè)對(duì)象年齡 (Age) 計(jì)數(shù)器。如果對(duì)象在 Eden 出生并經(jīng)過(guò)第一次 Minor GC 后仍然存活,并且能被 Survivor 容納的話,將被移動(dòng)到 Survivor 空間中,并且對(duì)象年齡設(shè)為 1。對(duì)象在 Survivor 區(qū)中每 “熬過(guò)” 一次 Minor GC, 年齡就增加 1 歲,當(dāng)它的年齡增加到一定程度(默認(rèn) 15 歲)會(huì)被晉升到老年代中。

對(duì)象晉升老年代的年齡閾值,可以通過(guò)參數(shù)據(jù) - XX : MaxTenuringThreshold 設(shè)置 動(dòng)態(tài)對(duì)象年齡判定 為了能更好地適應(yīng)不同程序的內(nèi)存狀況,虛擬機(jī)并不是水遠(yuǎn)地要求對(duì)象的年齡必須達(dá)到了 MaxTenuringThreshold 才能晉升老年代,如果在 Survivor 空間中相同年齡所有對(duì)象大小的總和大于 Survivor 空間的一半,年齡大于或等于該年齡的對(duì)象就可以直接進(jìn)入老年代,無(wú)須等到 MaxTenuringThreshold 中要求的年齡。

空間分配擔(dān)保 在發(fā)生 Minor GC 之前,虛擬機(jī)會(huì)先檢査老年代最大可用的連續(xù)空間是否大于新生代所有對(duì)象總空間,如果這個(gè)條件成立,那么 Minor GC 可以確保是安全的。如果不成立,則虛擬機(jī)會(huì)查看 HandlePromotionFailure 設(shè)置值是否允許擔(dān)保失敗。

如果允許,那么會(huì)繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對(duì)象的平均大小,如果大于,將嘗試著進(jìn)行一次 Minor GC, 盡管這次 Minor GC 是有風(fēng)險(xiǎn)的;如果小于,或者 HandlePromotionFailure 設(shè)置不允許冒險(xiǎn),那這時(shí)也要改為進(jìn)行一次 Full GC. 為什么要擔(dān)保: Minor GC 后還有大量對(duì)象存活且空間不夠存放新對(duì)象,就要直接在老年代存放 為什么是歷次晉升到老年代對(duì)象的平均大小: 取平均值進(jìn)行比較其實(shí)仍然是一種動(dòng)態(tài)概率的手段,也就是說(shuō),如果某次 Minor GCd 存活后的對(duì)象突增,遠(yuǎn)遠(yuǎn)高于平均值的話,依然會(huì)導(dǎo)致?lián)J?(HandlePromotionFailure) 如果出現(xiàn)了 HandlePromotionFailure 失敗,那就只好在失敗后重新發(fā)起一次 Full GC。

雖然擔(dān)保失敗時(shí)繞的子是最大的,但大部分情況下都還是會(huì)將 HandlePromotionFailure 開(kāi)關(guān)打開(kāi),避免 Full GC 過(guò)于頻繁。 eden 的大小范圍默認(rèn)是 =【-XX:G1NewSizePercent,-XX:G1MaxNewSizePercent】=【整堆 5%,整堆 60%】 humongous 如果一個(gè)對(duì)象的大小已經(jīng)超過(guò) Region 大小的 50% 了,那么就會(huì)被放入大對(duì)象專門的 Region 中,這種 Region 我們叫 humongous

G1 垃圾回收流程

?cec93e0e-aa86-11ed-bfe3-dac502259ad0.png?

網(wǎng)上對(duì) G1 的回收階段有不同的說(shuō)法,參考 Oracle JVM 工程師的一個(gè)說(shuō)法: 他把整個(gè) G1 的垃圾回收階段分成了這么三個(gè),第一個(gè)叫 Minor GC,就是對(duì)新生代的垃圾收集,第二個(gè)階段呢叫 Minor GC + Concurrent Mark,就是新生代的垃圾收集同時(shí)呢會(huì)執(zhí)行一些并發(fā)的標(biāo)記,這是第二個(gè)階段,第三個(gè)階段呢它叫 Mixed GC 混合收集,這三個(gè)階段是一個(gè)循環(huán)的過(guò)程。

剛開(kāi)始是這個(gè)新生代的垃圾收集,經(jīng)過(guò)一段時(shí)間,當(dāng)老年代的內(nèi)存超過(guò)一個(gè)閾值了,它會(huì)在新生代垃圾收集的同時(shí)進(jìn)行并發(fā)的標(biāo)記,等這個(gè)階段完成了以后,它會(huì)進(jìn)行一個(gè)混合收集,混合收集就是會(huì)對(duì)新生代、幸存區(qū)還有老年代都來(lái)進(jìn)行一個(gè)規(guī)模較大的一次收集,等內(nèi)存釋放掉了,混合收集結(jié)束。這時(shí)候伊甸園的內(nèi)存都被釋放掉,它會(huì)再次進(jìn)入新生代的一個(gè)垃圾收集過(guò)程,那我們先來(lái)看看這個(gè)新生代的收集 Minor GC。

Minor GC 的回收過(guò)程(eden 滿了回收)

選定所有 Eden Region 放入 CSet,使用多線程復(fù)制算法將 CSet 的存活對(duì)象復(fù)制到 Survivor Region 或者晉升到 Old Region。 下圖分 7 步演示了這個(gè)過(guò)程 1 初始狀態(tài),堆無(wú)占用 2 Eden Region 滿了進(jìn)行標(biāo)記 3 將存活對(duì)象復(fù)制到 Survivor Region 4 清理 Eden Region 5 Eden Region 又滿了進(jìn)行再次標(biāo)記,此時(shí)會(huì)連帶 Survivor Region 一起標(biāo)記 6 將存活對(duì)象復(fù)制到另一個(gè) Survivor Region 7 再次清理 Eden Region 和被標(biāo)記過(guò)的 Survivor Region ?

cee18112-aa86-11ed-bfe3-dac502259ad0.png Minor GC 結(jié)束后自動(dòng)進(jìn)行并發(fā)標(biāo)記,為以后可能的 Mixed GC 做準(zhǔn)備

Mixed GC 的回收過(guò)程(專注垃圾最多的分區(qū))

選定所有 Eden Region 和全局并發(fā)標(biāo)記計(jì)算得到的收益較高的部分 Old Region 放入 CSet,使用多線程復(fù)制算法將 CSet 的存活對(duì)象復(fù)制到 Survivor Region 或者晉升到 Old Region。 當(dāng)堆空間的占用率達(dá)到一定閾值后會(huì)觸發(fā) Mixed GC(默認(rèn) 45%,由參數(shù)決定) Mixed GC 它一定會(huì)回收年輕代,并會(huì)采集部分老年代的 Region 進(jìn)行回收的,所以它是一個(gè) “混合” GC。 下圖分 3 步演示了這個(gè)過(guò)程 1 并發(fā)標(biāo)記所有 Region 2 并發(fā)復(fù)制 3 并發(fā)清理 ?

cefbfede-aa86-11ed-bfe3-dac502259ad0.png?

ZGC


ZGC(Z Garbage Collector) 是一款性能比 G1 更加優(yōu)秀的垃圾收集器。ZGC 第一次出現(xiàn)是在 JDK 11 中以實(shí)驗(yàn)性的特性引入,這也是 JDK 11 中最大的亮點(diǎn)。在 JDK 15 中 ZGC 不再是實(shí)驗(yàn)功能,可以正式投入生產(chǎn)使用了。

目標(biāo)低延遲

?保證最大停頓時(shí)間在幾毫秒之內(nèi),不管你堆多大或者存活的對(duì)象有多少。

?可以處理 8MB-16TB 的堆

通過(guò)以上歷代垃圾回收器的講解,我們大致了解到減少延遲的底層思想不外乎將 stop the world 進(jìn)行極限壓縮,將能并行的部分全部采用和用戶線程并行的方式處理,然而 ZGC 更 "過(guò)分" 它甚至把一分部垃圾回收的工作交給了用戶線程去做,那么它是怎么做到的呢?ZGC 的標(biāo)記和清理工作同 CMS、G1 大致差不多,仔細(xì)看下圖的過(guò)程,和 CMS 特別像,這就是我在上文說(shuō)的 CMS 其實(shí)并沒(méi)有真正被拋棄,它的部分思想在 ZGC 有發(fā)揚(yáng)。

??cf19482c-aa86-11ed-bfe3-dac502259ad0.png

ZGC 的步驟大致可分為三大階段分別是標(biāo)記、轉(zhuǎn)移、重定位。 標(biāo)記:從根開(kāi)始標(biāo)記所有存活對(duì)象 轉(zhuǎn)移:選擇部分活躍對(duì)象轉(zhuǎn)移到新的內(nèi)存空間上 重定位:因?yàn)閷?duì)象地址變了,所以之前指向老對(duì)象的指針都要換到新對(duì)象地址上。 并且這三個(gè)階段都是并發(fā)的。

初始轉(zhuǎn)移需要掃描 GC Roots 直接引用的對(duì)象并進(jìn)行轉(zhuǎn)移,這個(gè)過(guò)程需要 STW,STW 時(shí)間跟 GC Roots 成正比。 并發(fā)轉(zhuǎn)移準(zhǔn)備 :分析最有回收價(jià)值 GC 分頁(yè)(無(wú) STW) 初始轉(zhuǎn)移應(yīng)對(duì)初始標(biāo)記的數(shù)據(jù) 并發(fā)轉(zhuǎn)移應(yīng)對(duì)并發(fā)標(biāo)記的數(shù)據(jù) 除了標(biāo)記清理過(guò)程繼承了 CMS 和 G1 的思想,ZGC 要做了以下優(yōu)化

并發(fā)清理(轉(zhuǎn)移對(duì)象)

在 CMS 和 G1 中都用到了寫屏障,而 ZGC 用到了讀屏障。 寫屏障是在對(duì)象引用賦值時(shí)候的 AOP,而讀屏障是在讀取引用時(shí)的 AOP。 比如Object a = obj.foo;,這個(gè)過(guò)程就會(huì)觸發(fā)讀屏障。

也正是用了讀屏障,ZGC 可以并發(fā)轉(zhuǎn)移對(duì)象,而 G1 用的是寫屏障,所以轉(zhuǎn)移對(duì)象時(shí)候只能 STW。 簡(jiǎn)單的說(shuō)就是 GC 線程轉(zhuǎn)移對(duì)象之后,應(yīng)用線程讀取對(duì)象時(shí),可以利用讀屏障通過(guò)指針上的標(biāo)志來(lái)判斷對(duì)象是否被轉(zhuǎn)移。

讀屏障會(huì)對(duì)應(yīng)用程序的性能有一定影響,據(jù)測(cè)試,對(duì)性能的最高影響達(dá)到 4%,但提高了 GC 并發(fā)能力,降低了 STW。這就是上面所說(shuō)的 ZGC “過(guò)分” 地將部分垃圾回收工作交給用戶線程的原因。

染色指針

染色指針其實(shí)就是從 64 位的指針中,拿幾位來(lái)標(biāo)識(shí)對(duì)象此時(shí)的情況,分別表示 Marked0、Marked1、Remapped、Finalizable。 ?

cf363c8e-aa86-11ed-bfe3-dac502259ad0.png

0-41 這 42 位就是正常的地址,所以說(shuō) ZGC 最大支持 4TB (理論上可以 16TB) 的內(nèi)存,因?yàn)榫?42 位用來(lái)表示地址 也因此 ZGC 不支持 32 位指針,也不支持指針壓縮。

其實(shí)對(duì)象只需要兩個(gè)狀態(tài) Marked,Remapped,對(duì)象被標(biāo)記了,對(duì)象被重新映射了,為什么會(huì)有 M0,M1,用來(lái)區(qū)分本次 GC 標(biāo)記和上次 GC 標(biāo)記 以下是標(biāo)記轉(zhuǎn)移算法說(shuō)明: 1 在垃圾回收開(kāi)始前:Remapped 2 標(biāo)記過(guò)程: 標(biāo)記線程訪問(wèn) 發(fā)現(xiàn)對(duì)象地址視圖是 Remapped 這時(shí)候?qū)⒅羔槝?biāo)記為 M0 發(fā)現(xiàn)對(duì)象地址視圖是 M0,則說(shuō)明這個(gè)對(duì)象是標(biāo)記開(kāi)始之后新分配的或者已經(jīng)標(biāo)記過(guò)的對(duì)象,所以無(wú)需處理 應(yīng)用線程 如果創(chuàng)建新對(duì)象,則將其地址視圖置為 M0 3 標(biāo)記階段結(jié)束后 ZGC 會(huì)使用一個(gè)對(duì)象活躍表來(lái)存儲(chǔ)這些對(duì)象地址,此時(shí)活躍的對(duì)象地址視圖是 M0 4 并發(fā)轉(zhuǎn)移階段 轉(zhuǎn)移線程: 轉(zhuǎn)移成功后對(duì)象地址視圖被置為 Remapped(也就是說(shuō) GC 線程如果訪問(wèn)到對(duì)象,此時(shí)對(duì)象地址視圖是 M0,并且存在或活躍表中,則將其轉(zhuǎn)移,并將地址視圖置為 Remapped ) 如果在活躍表中,但是地址視圖已經(jīng)是 Remapped 說(shuō)明已經(jīng)被轉(zhuǎn)移了,不做處理。

應(yīng)用線程: 如果創(chuàng)建新對(duì)象,地址視圖會(huì)設(shè)為 Remapped 5 下次標(biāo)記使用 M1 M1 標(biāo)識(shí)本次垃圾回收中活躍的對(duì)象 M0 是上一次回收被標(biāo)記的對(duì)象,但是沒(méi)有被轉(zhuǎn)移,且在本次回收中也沒(méi)有被標(biāo)記活躍的對(duì)象。

下圖展示了 Marked,Remapped 的過(guò)程, 初始化時(shí) A,B,C 三個(gè)對(duì)象處于 Remapped 狀態(tài) 第一次 GC,A 被轉(zhuǎn)移,B 未被轉(zhuǎn)移,C 無(wú)引用將被回收 第二次 GC,由于 A 被轉(zhuǎn)移過(guò)了(Remapped 狀態(tài)),所以被標(biāo)記 M1,此時(shí)恰好 B 為不活躍對(duì)象,將被清理 第三次 GC,A 又被標(biāo)記成 M0 ??

cf5b2828-aa86-11ed-bfe3-dac502259ad0.png

多重映射

Marked0、Marked1 和 Remapped 三個(gè)視圖 ZGC 為了能高效、靈活地管理內(nèi)存,實(shí)現(xiàn)了兩級(jí)內(nèi)存管理:虛擬內(nèi)存和物理內(nèi)存,并且實(shí)現(xiàn)了物理內(nèi)存和虛擬內(nèi)存的映射關(guān)系 在 ZGC 中這三個(gè)空間在同一時(shí)間點(diǎn)有且僅有一個(gè)空間有效,利用虛擬空間換時(shí)間,這三個(gè)空間的切換是由垃圾回收的不同階段觸發(fā)的,通過(guò)限定三個(gè)空間在同一時(shí)間點(diǎn)有且僅有一個(gè)空間有效高效的完成 GC 過(guò)程的并發(fā)操作 ?

cf78281a-aa86-11ed-bfe3-dac502259ad0.png

支持 NUMA

NUMA 是非一致內(nèi)存訪問(wèn)的縮寫 (Non-Uniform Memory Access,NUMA) 早年如下圖:SMP 架構(gòu) (Symmetric Multi-Processor),因?yàn)槿我粋€(gè) CPU 對(duì)內(nèi)存的訪問(wèn)速度是一致的,不用考慮不同內(nèi)存地址之間的差異,所以也稱一致內(nèi)存訪問(wèn)(Uniform Memory Access, UMA )。這個(gè)核心越加越多,漸漸的總線和北橋就成為瓶頸,那不能夠啊,于是就想了個(gè)辦法。 ?

cf8f72b8-aa86-11ed-bfe3-dac502259ad0.png ??

把 CPU 和內(nèi)存集成到一個(gè)單元上,這個(gè)就是非一致內(nèi)存訪問(wèn) (Non-Uniform Memory Access,NUMA)。

cfa97686-aa86-11ed-bfe3-dac502259ad0.png

?ZGC 對(duì) NUMA 的支持是小分區(qū)分配時(shí)會(huì)優(yōu)先從本地內(nèi)存分配,如果本地內(nèi)存不足則從遠(yuǎn)程內(nèi)存分配。

ZGC 優(yōu)劣

綜上分析,ZGC 在戰(zhàn)略上沿用了上幾代 GC 的算法策略,采用并發(fā)標(biāo)記,并發(fā)清理的思路,在戰(zhàn)術(shù)上,通過(guò)染色指針、多重映射,讀屏障等優(yōu)化達(dá)到更理想的并發(fā)清理,通過(guò)支持 NUMA 達(dá)到了更快的內(nèi)存操作。但 ZGC 同樣不是銀彈,它也有自身的優(yōu)缺點(diǎn),如下

優(yōu)勢(shì):

1、一旦某個(gè) Region 的存活對(duì)象被移走之后,這個(gè) Region 立即就能夠被釋放和重用掉,而不必等待整個(gè)堆中所有指向該 Region 的引用都被修正后才能清理,這使得理論上只要還有一個(gè)空閑 Region,ZGC 就能完成收集。

2、顏色指針可以大幅減少在垃圾收集過(guò)程中內(nèi)存屏障的使用數(shù)量,ZGC 只使用了讀屏障。

3、顏色指針具備強(qiáng)大的擴(kuò)展性,它可以作為一種可擴(kuò)展的存儲(chǔ)結(jié)構(gòu)用來(lái)記錄更多與對(duì)象標(biāo)記、重定位過(guò)程相關(guān)的數(shù)據(jù),以便日后進(jìn)一步提高性能。

劣勢(shì):

1、它能承受的對(duì)象分配速率不會(huì)太高 ZGC 準(zhǔn)備要對(duì)一個(gè)很大的堆做一次完整的并發(fā)收集。在這段時(shí)間里面,由于應(yīng)用的對(duì)象分配速率很高,將創(chuàng)造大量的新對(duì)象,這些新對(duì)象很難進(jìn)入當(dāng)次收集的標(biāo)記范圍,通常就只能全部當(dāng)作存活對(duì)象來(lái)看待 —— 盡管其中絕大部分對(duì)象都是朝生夕滅的,這就產(chǎn)生了大量的浮動(dòng)垃圾。

如果這種高速分配持續(xù)維持的話,每一次完整的并發(fā)收集周期都會(huì)很長(zhǎng),回收到的內(nèi)存空間持續(xù)小于期間并發(fā)產(chǎn)生的浮動(dòng)垃圾所占的空間,堆中剩余可騰挪的空間就越來(lái)越小了。目前唯一的辦法就是盡可能地增加堆容量大小,獲得更多喘息的時(shí)間。

2、吞吐量低于 G1 GC 一般來(lái)說(shuō),可能會(huì)下降 5%-15%。對(duì)于堆越小,這個(gè)效應(yīng)越明顯,堆非常大的時(shí)候,比如 100G,其他 GC 可能一次 Major 或 Full GC 要幾十秒以上,但是對(duì)于 ZGC 不需要那么大暫停。這種細(xì)粒度的優(yōu)化帶來(lái)的副作用就是,把很多環(huán)節(jié)其他 GC 里的 STW 整體處理,拆碎了,放到了更大時(shí)間范圍內(nèi)里去跟業(yè)務(wù)線程并發(fā)執(zhí)行,甚至?xí)苯幼寴I(yè)務(wù)線程幫忙做一些 GC 的操作,從而降低了業(yè)務(wù)線程的處理能力。

總結(jié)

綜上,其實(shí) ZGC 并不是一個(gè)憑空冒出的全新垃圾回收,它結(jié)合前幾代 GC 的思想,同時(shí)在戰(zhàn)術(shù)上做了優(yōu)化以達(dá)到極限的 STW,ZGC 的優(yōu)秀表現(xiàn)有可能會(huì)改變未來(lái)程序編寫方式,站在垃圾收集器的角度,垃圾收集器特別喜歡不可變對(duì)象,原有編程方式鑒于內(nèi)存、GC 能力所限使用可變對(duì)象來(lái)復(fù)用對(duì)象而不是銷毀重建,試想如果有了 ZGC 的強(qiáng)大回收能力的加持,是不是我們就可以無(wú)腦的使用不可變對(duì)象進(jìn)行代碼編寫






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 計(jì)數(shù)器
    +關(guān)注

    關(guān)注

    32

    文章

    2261

    瀏覽量

    94986
  • cms
    cms
    +關(guān)注

    關(guān)注

    0

    文章

    60

    瀏覽量

    10999
  • JVM
    JVM
    +關(guān)注

    關(guān)注

    0

    文章

    158

    瀏覽量

    12261
  • 虛擬機(jī)
    +關(guān)注

    關(guān)注

    1

    文章

    940

    瀏覽量

    28428
  • 收集器
    +關(guān)注

    關(guān)注

    0

    文章

    30

    瀏覽量

    3189

原文標(biāo)題:從歷代GC算法角度刨析ZGC

文章出處:【微信號(hào):OSC開(kāi)源社區(qū),微信公眾號(hào):OSC開(kāi)源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    jvm的類加載器的整體結(jié)構(gòu)及過(guò)程解析

    前言 我們很多小伙伴平時(shí)都是做JAVA開(kāi)發(fā)的,那么作為一名合格的工程師,你是否有仔細(xì)的思考過(guò)JVM的運(yùn)行原理。 如果懂得了JVM的運(yùn)行原理和內(nèi)存模型,像是一些JVM調(diào)優(yōu)、
    的頭像 發(fā)表于 09-27 15:49 ?3438次閱讀
    <b class='flag-5'>jvm</b>的類加載器的整體結(jié)構(gòu)及過(guò)程解析

    Jvm的整體結(jié)構(gòu)和特點(diǎn)

      一、虛擬機(jī)簡(jiǎn)介  1、虛擬機(jī)概念  虛擬機(jī)(Virtual Machine)指通過(guò)軟件模擬的具有完整硬件系統(tǒng)功能的、運(yùn)行在一個(gè)完全隔離環(huán)境中的完整計(jì)算機(jī)系統(tǒng)。在實(shí)體計(jì)算機(jī)中能夠完成的工作在虛擬機(jī)
    發(fā)表于 01-05 17:23

    Jvm垃圾回收機(jī)制及性能調(diào)優(yōu)實(shí)戰(zhàn)

    JVM中自動(dòng)檢測(cè)并移除不再使用的數(shù)據(jù)對(duì)象的這種機(jī)制稱為:垃圾回收,簡(jiǎn)稱GC。JVM通過(guò)使用垃圾收集器及使用相應(yīng)的
    發(fā)表于 04-03 14:31 ?2次下載

    帶顏色的JVM垃圾回收三色標(biāo)記法

    三色標(biāo)記法是一種垃圾回收法,它可以讓JVM不發(fā)生或僅短時(shí)間發(fā)生STW(Stop The World),從而達(dá)到清除JVM內(nèi)存垃圾的目的。JVM
    的頭像 發(fā)表于 10-20 14:23 ?1681次閱讀

    詳解JVM垃圾回收算法和垃圾回收器

    JVM 垃圾回收機(jī)制是對(duì)堆中沒(méi)有使用的對(duì)象進(jìn)行回收,那么判斷對(duì)象是否“存活”就至關(guān)重要。在判斷對(duì)象是否“存活”的方法中,我們會(huì)介紹引用計(jì)數(shù)算法和可達(dá)性分析法。
    的頭像 發(fā)表于 03-29 13:55 ?1538次閱讀
    詳解<b class='flag-5'>JVM</b>的<b class='flag-5'>垃圾</b>回收算法和<b class='flag-5'>垃圾</b>回收器

    JVM內(nèi)存布局的多方面了解

      JVM內(nèi)存布局規(guī)定了Java在運(yùn)行過(guò)程中內(nèi)存申請(qǐng)、分配、管理的策略,保證了JVM的穩(wěn)定高效運(yùn)行。不同的JVM對(duì)于內(nèi)存的劃分方式和管理機(jī)制存在部分差異。結(jié)合
    發(fā)表于 07-08 15:09 ?429次閱讀

    JVM入門之垃圾回收算法

    根據(jù)如何判定對(duì)象是垃圾,垃圾回收算法分為兩類:1、 「引用計(jì)數(shù)式垃圾收集」 (判定垃圾是通過(guò)引用計(jì)數(shù)器)別名:直接垃圾收集 2、 「追蹤式
    的頭像 發(fā)表于 02-10 11:40 ?863次閱讀
    <b class='flag-5'>JVM</b>入門之<b class='flag-5'>垃圾</b>回收算法

    JVM內(nèi)存布局詳解

    JVM內(nèi)存布局規(guī)定了Java在運(yùn)行過(guò)程中內(nèi)存申請(qǐng)、分配、管理的策略,保證了JVM的穩(wěn)定高效運(yùn)行。不同的JVM對(duì)于內(nèi)存的劃分方式和管理機(jī)制存在部分差異。結(jié)合
    的頭像 發(fā)表于 04-26 10:10 ?570次閱讀
    <b class='flag-5'>JVM</b>內(nèi)存布局詳解

    詳細(xì)解析JVM中的垃圾回收機(jī)制

    Java語(yǔ)言的一大優(yōu)勢(shì)在于其具有自動(dòng)垃圾回收(Garbage Collection,GC)機(jī)制,讓開(kāi)發(fā)者無(wú)需關(guān)心內(nèi)存的分配與釋放。
    的頭像 發(fā)表于 06-06 16:53 ?2104次閱讀

    垃圾收集器的JVM參數(shù)配置

    本篇文章我們就來(lái)給大家介紹垃圾收集器的 JVM 參數(shù)配置。 JVM參數(shù)有很多,其實(shí)我們直接使用默認(rèn)的JVM參數(shù),不去修改都可以滿足大多數(shù)情況。但是如果你想在有限的硬件資源下,部署的系統(tǒng)
    的頭像 發(fā)表于 10-09 16:35 ?598次閱讀
    <b class='flag-5'>垃圾</b>收集器的<b class='flag-5'>JVM</b>參數(shù)配置

    jvm調(diào)優(yōu)參數(shù)

    JVM(Java虛擬機(jī))是Java程序的運(yùn)行環(huán)境,它負(fù)責(zé)解釋Java字節(jié)碼并執(zhí)行相應(yīng)的指令。為了提高應(yīng)用程序的性能和穩(wěn)定性,我們可以調(diào)優(yōu)JVM的參數(shù)。 JVM調(diào)優(yōu)主要涉及到堆內(nèi)存、垃圾
    的頭像 發(fā)表于 12-05 11:29 ?696次閱讀

    jvm參數(shù)的設(shè)置和jvm調(diào)優(yōu)

    JVM(Java虛擬機(jī))參數(shù)的設(shè)置和調(diào)優(yōu)對(duì)于提高Java應(yīng)用程序的性能和穩(wěn)定性非常重要。在本文中,我們將詳細(xì)介紹JVM參數(shù)的設(shè)置和調(diào)優(yōu)方法。 一、JVM參數(shù)的設(shè)置 內(nèi)存參數(shù): -Xms:設(shè)置J
    的頭像 發(fā)表于 12-05 11:36 ?1661次閱讀

    jvm配置的mx

    用于設(shè)置JVM的最大堆內(nèi)存大小,即堆的上限。當(dāng)堆內(nèi)存不足時(shí),JVM會(huì)觸發(fā)垃圾回收機(jī)制以釋放內(nèi)存。如果垃圾回收無(wú)法釋放足夠的內(nèi)存,
    的頭像 發(fā)表于 12-05 14:24 ?760次閱讀

    weblogic jvm參數(shù)配置

    在WebLogic中,JVM參數(shù)配置是非常重要的,它可以對(duì)應(yīng)用程序的性能和穩(wěn)定性產(chǎn)生直接影響。JVM參數(shù)通過(guò)調(diào)整Java虛擬機(jī)的運(yùn)行時(shí)行為,可以優(yōu)化內(nèi)存管理、垃圾回收以及線程管理等方面的性能。 首先
    的頭像 發(fā)表于 12-05 14:31 ?1530次閱讀

    從原理聊JVM(一):染色標(biāo)記和垃圾回收算法

    導(dǎo)讀 JAVA簡(jiǎn)單易用的特性,能夠讓研發(fā)人員在不了解JVM的底層運(yùn)行機(jī)制的情況下依舊能夠編寫出功能完善的代碼。 但是對(duì)JVM的理解,是一個(gè)程序員普通和優(yōu)秀的分水嶺。全面地了解JVM
    的頭像 發(fā)表于 08-20 15:25 ?289次閱讀
    從原理聊<b class='flag-5'>JVM</b>(一):染色標(biāo)記和<b class='flag-5'>垃圾</b>回收算法
    主站蜘蛛池模板: 久草视频一区 | 成人爽a毛片在线视频 | 日本三级日本三级日本三级极 | wwwxx在线| 亚洲午夜顶级嘿嘿嘿影院 | 月夜免费观看高清在线完整 | 精彩视频一区二区三区 | 手机看片免费福利 | 男人扒开美女尿口无遮挡图片 | 成年人视频黄色 | 久久久久久午夜精品 | 国产午夜毛片一区二区三区 | 美女扒开内裤无遮挡禁18 | 夜夜爽66| 欧美aaa| 国产做a爰片久久毛片 | 男人都懂的网址在线看片 | www天堂网 | 欧美成人性高清观看 | 欧美成人性色区 | 一级片免费在线观看视频 | 国产免费一级高清淫曰本片 | 色四虎| 亚洲成年人影院 | www我要色综合com | 美女被强插 | 欧美影院一区二区三区 | 永久免费观看视频 | 四虎精品免费永久在线 | 四虎永久精品免费观看 | 欧美黄色大片免费 | 亚洲高清成人 | 精品福利在线观看 | 国产精品福利一区二区亚瑟 | 久久婷五月综合 | 99 久久99久久精品免观看 | 日韩特黄 | 亚洲电影免费 | 99热这里精品 | 大尺度在线| 手机在线一区二区三区 |