溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

GC過程中需要stop the world的原因是什么

發(fā)布時(shí)間:2021-10-13 10:32:47 來源:億速云 閱讀:487 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“GC過程中需要stop the world的原因是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“GC過程中需要stop the world的原因是什么”吧!

常用回收算法

    標(biāo)記-清除   ( mark-sweep )

優(yōu)點(diǎn):開銷低,速度快
缺點(diǎn): 原地清理所以無法避免碎片問題

    標(biāo)記-復(fù)制    ( mark-copy )

優(yōu)點(diǎn):GC后的內(nèi)存空間是連續(xù)的缺點(diǎn): 可用內(nèi)存空間減半

    標(biāo)記-整理    ( mark-compact)

優(yōu)點(diǎn):無碎片問題,內(nèi)存空間可用大小不減半
缺點(diǎn):效率低,開銷大

    引用-計(jì)數(shù)    (reference counting)

前三種垃圾回收算法都是間接式的,它們都需要從已知的根集合出發(fā)對(duì)存活對(duì)象圖進(jìn)行遍歷,進(jìn)而才能確定所有的存活對(duì)象。
在引用計(jì)數(shù)中,對(duì)象的存活性可以通過引用關(guān)系的創(chuàng)建或刪除直接判定,從而無須像追蹤式回收器那樣先通過堆遍歷找出所有的存活對(duì)象,然后再反向確定出未遍歷的垃圾對(duì)象。

優(yōu)點(diǎn):直接遍歷,速度快
缺點(diǎn):無法解決環(huán)形引用問題

GC為什么要分代回收

對(duì)傳統(tǒng)的、基本的GC實(shí)現(xiàn)來說,由于它們?cè)贕C的整個(gè)工作過程中都要“stop-the-world”,如果能想辦法縮短GC一次工作的時(shí)間長度就是件重要的事情。如果說收集整個(gè)GC堆耗時(shí)太長,那不如只收集其中的一部分?

在對(duì)象數(shù)量較少的情況下,追蹤式垃圾回收器(特別是復(fù)制式垃圾回收器)能夠最高效地進(jìn)行垃圾回收。但長壽對(duì)象的存在卻會(huì)影響回收效率,因?yàn)榛厥掌鞑皇欠磸?fù)地對(duì)其進(jìn)行標(biāo)記、追蹤,就是反復(fù)地把它們從一個(gè)半?yún)^(qū)復(fù)制到另一個(gè)半?yún)^(qū)。

根據(jù)程序?qū)嶋H運(yùn)行的情況,jvm關(guān)于垃圾回收有2條假說(即2條經(jīng)驗(yàn)法則)

1)弱分代假說:絕大多數(shù)對(duì)象的生命周期都很短,絕大多數(shù)的對(duì)象都是朝生夕滅的。

2)強(qiáng)分代假說:熬過越多次垃圾收集過程的對(duì)象越難以消亡。

現(xiàn)在絕大多數(shù)的jvm都遵循了分代回收理論來設(shè)計(jì)。jvm的堆被分成至少兩個(gè)區(qū)域,新生代(young)和 老年代(old)。新生代存放才創(chuàng)建的對(duì)象,老年代存放少量存活的對(duì)象。

在新生代,對(duì)象“朝生夕死”。gc一般采取復(fù)制算法,因?yàn)榇怂惴ǖ耐怀鎏攸c(diǎn)就是只關(guān)心哪些需要被復(fù)制,可達(dá)性分析只用標(biāo)記和復(fù)制很少的存活對(duì)象。不用遍歷整個(gè)堆,因?yàn)榇蟛糠侄际且獊G棄的。但是其缺點(diǎn)也很明顯,需要浪費(fèi)一半的內(nèi)存空間

所以針對(duì)老年代對(duì)象的特點(diǎn),一般采用標(biāo)記-清理(有的算法會(huì)帶壓縮)策略的算法。這部分如果采用復(fù)制算法的話,一方面沒有額外空間給其擔(dān)保,另一方面由于存活率高,復(fù)制的開銷顯著增大 。

GC過程中需要stop the world的原因是什么 

一個(gè)對(duì)象從出生到消亡

GC過程中需要stop the world的原因是什么

一個(gè)對(duì)象產(chǎn)生之后首先進(jìn)行棧上分配,棧上如果分配不下會(huì)進(jìn)入伊甸區(qū),伊甸區(qū)經(jīng)過一次垃圾回收之后進(jìn)入surivivor區(qū),survivor區(qū)在經(jīng)過一次垃圾回收之后又進(jìn)入另外一個(gè)survivor,與此同時(shí)伊甸區(qū)的某些對(duì)象也跟著進(jìn)入另外一個(gè)survivot,什么時(shí)候年齡夠了就會(huì)進(jìn)入old區(qū),這是整個(gè)對(duì)象的一個(gè)邏輯上的移動(dòng)過程。

那什么時(shí)候會(huì)在棧上分配,什么時(shí)候會(huì)在伊甸區(qū)分配?

1 棧上分配

棧上分配:

  • 線程私有小對(duì)象:小對(duì)象、線程私有的

  • 無逃逸:就在某一段代碼中使用,除了這段代碼就沒有人認(rèn)識(shí)它了

  • 支持標(biāo)量替換:意思是用普通的屬性、把普通的類型代替對(duì)象就叫標(biāo)量替換

棧上分配會(huì)比在堆上分配快一點(diǎn),如果在棧上分配不下,會(huì)優(yōu)先進(jìn)行本地分配,也就是 線程本地分配TLAB(Thread local Allocation Buffer): 在伊甸區(qū)很多線程都會(huì)往里面分配對(duì)象,但是分配對(duì)象的時(shí)候我們一定會(huì)進(jìn)行空間的征用,誰搶到算誰的,多線程的同步,效率就會(huì)降低,所以設(shè)計(jì)了TLAB機(jī)制

  • 占用eden,默認(rèn)為1%,在伊甸區(qū)取用百分之一的空間,這塊空間叫做線程獨(dú)有,分配對(duì)象的時(shí)候首先往線程獨(dú)有的這塊空間進(jìn)行分配

  • 多線程的時(shí)候不用競爭eden就可以申請(qǐng)空間,提高效率

2 老年代

對(duì)象什么時(shí)候進(jìn)入老年代?

回收了多少次進(jìn)入老年代?

  • 超過 XX:MaxTenuringThreshold指定次數(shù)(YGC)

    1. Parallel Scavenge 15次進(jìn)入老年代

    2. CMS 6次進(jìn)入老年代

    3. G1 15次進(jìn)入老年代

網(wǎng)上有說可以次數(shù)往上調(diào)大,這個(gè)是不可能的

動(dòng)態(tài)年齡判斷

為了能夠適用不同程序的內(nèi)存狀況,虛擬機(jī)并不是永遠(yuǎn)的要求對(duì)象的年齡必須達(dá)到了MaxTenuringThreshold才能晉升老年代,如果在Surivivor空間中相同年齡所有對(duì)象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對(duì)象就可以直接進(jìn)入老年代,無需等到MaxTenuringThreshold中要求的年齡。

兩個(gè)Survivor之間拷貝來拷貝去只要超過百分之50的時(shí)候把年齡最大的直接放入到old區(qū),也就是不一定非得到15歲。

在s1里面有這么多對(duì)象拷貝到了s2里面超過百分之50的話,s1里面在加上伊甸區(qū)里面,整個(gè)一個(gè)對(duì)象一下子拷貝到s2里面,經(jīng)過一次垃圾回收,過去之后,這個(gè)時(shí)候整個(gè)加起來對(duì)象已經(jīng)超過s2的一半了,這里面年齡最大的一些對(duì)象直接進(jìn)入老年區(qū),這個(gè)就叫做動(dòng)態(tài)年輕判斷

GC過程中需要stop the world的原因是什么

大對(duì)象直接進(jìn)入老年代 

    所謂的大對(duì)象是指,需要連續(xù)大量內(nèi)存空間的Java對(duì)象,最典型的大對(duì)象就是那種很長的字符串以及數(shù)組,經(jīng)常出現(xiàn)大對(duì)象容易導(dǎo)致內(nèi)存還有不少空間的時(shí)候就提前觸發(fā)了垃圾收集來獲得足夠的連續(xù)內(nèi)存空間

GC過程中需要stop the world的原因是什么

start 先是new一個(gè)對(duì)象,然后在棧上進(jìn)行分配,如果在棧上能夠分配,就分配到棧上,棧直接彈出,彈出結(jié)束,如果在棧上分配不下,判斷對(duì)象是否為大對(duì)象,如果是大對(duì)象,直接進(jìn)入老年代,F(xiàn)GC后結(jié)束如果不是,進(jìn)入線程本地分配(TLAB),不管怎么樣都會(huì)到伊甸區(qū)進(jìn)行GC清除,如果清除完畢,直接結(jié)束,如果沒有清除完畢,進(jìn)入S1,S1繼續(xù)GC清除,如果年齡到了進(jìn)入old區(qū),如果年齡不夠進(jìn)入S2,然后S2再繼續(xù)GC的清除,要么年齡到了,要么動(dòng)態(tài)年齡達(dá)到

MinorGC/YGC: 年輕代空間耗盡時(shí)觸發(fā)
MajorGC/FullGC: 在老年代無法繼續(xù)分配空間時(shí)觸發(fā),新生代老年代同時(shí)進(jìn)行回收

存在的問題-跨帶引用

    年輕代中的對(duì)象可能會(huì)引用老年代的對(duì)象,那么年輕代垃圾回收的時(shí)候怎么避免老年代全部掃描?

    解決辦法:記憶集 (remembered set)對(duì)應(yīng)實(shí)現(xiàn) 卡表(CardTable)

    GC Roots = 新生代 GCRoot + RememberedSet

常見的垃圾回收器

新生代收集器: Serial、ParNew、Parallel Scavenge

老年代收集器: Serial Old、CMS、Parallel Old

新生代和老年代收集器: G1、ZGC、Shenandoah

每種垃圾回收器之間不是獨(dú)立操作的,下圖表示垃圾回收器之間有連線表示,可以協(xié)作使用:

GC過程中需要stop the world的原因是什么

Serial

單線程串行  復(fù)制算法

Serial Old

老年代的收集器,與Serial一樣是單線程,不同的是算法用的是標(biāo)記-整理(Mark-Compact)

Parallel Scavenge

Serial 收集器的多線程版本,并行收集器。 復(fù)制算法

Parallel Old

老年代的收集器,是Parallel Scavenge老年代的版本。其中的算法替換成 Mark-Compact。

ParNew

跟Parallel類似,專門為了配合cms使用。 復(fù)制算法。 新生代并行收集器。

CMS

concurrent mark sweep 并發(fā)標(biāo)記清除,以獲取最短回收停頓時(shí)間為目標(biāo)。 老年代并發(fā)回收器。

CMS與三色標(biāo)記算法

CMS(Concurrent Mark Sweep)是一款里程碑式的垃圾收集器,為什么這么說呢?因?yàn)樵谒?,GC線程和用戶線程是無法同時(shí)工作的,即使是Parallel Scavenge,也不過是GC時(shí)開啟多個(gè)線程并行回收而已,GC的整個(gè)過程依然要暫停用戶線程,即Stop The World。這帶來的后果就是Java程序運(yùn)行一段時(shí)間就會(huì)卡頓一會(huì),降低應(yīng)用的響應(yīng)速度,這對(duì)于運(yùn)行在服務(wù)端的程序是不能被接收的。

GC時(shí)為什么要暫停用戶線程?
首先,如果不暫停用戶線程,就意味著期間會(huì)不斷有垃圾產(chǎn)生,永遠(yuǎn)也清理不干凈。
其次,用戶線程的運(yùn)行必然會(huì)導(dǎo)致對(duì)象的引用關(guān)系發(fā)生改變,這就會(huì)導(dǎo)致兩種情況:漏標(biāo)和錯(cuò)標(biāo)。

漏標(biāo)

原本不是垃圾,但是GC的過程中,用戶線程將其引用關(guān)系修改,導(dǎo)致GC Roots不可達(dá),成為了垃圾。 這種情況還好一點(diǎn),無非就是產(chǎn)生了一些浮動(dòng)垃圾,下次GC再清理就好了。

錯(cuò)標(biāo)

原本是垃圾,但是GC的過程中,用戶線程將引用重新指向了它,這時(shí)如果GC一旦將其回收,將會(huì)導(dǎo)致程序運(yùn)行錯(cuò)誤。

針對(duì)這些問題,CMS是如何解決的呢?它是如何做到GC線程和用戶線程并發(fā)工作的呢???

大概可分為四個(gè)主要步驟

初始標(biāo)記

只標(biāo)記GC Root 直接關(guān)聯(lián)的對(duì)象,時(shí)間很短, STW

并發(fā)標(biāo)記

從GC Root 直接關(guān)聯(lián)的對(duì)象開始遍歷整個(gè)對(duì)象圖,與用戶線程并行

重新標(biāo)記

修正并發(fā)標(biāo)記,三色標(biāo)記。 STW

并發(fā)清理

采用標(biāo)記清除算法,存在碎片問題

GC時(shí)為什么要暫停用戶線程?

首先,如果不暫停用戶線程,就意味著期間會(huì)不斷有垃圾產(chǎn)生,永遠(yuǎn)也清理不干凈。
其次,用戶線程的運(yùn)行必然會(huì)導(dǎo)致對(duì)象的引用關(guān)系發(fā)生改變,這就會(huì)導(dǎo)致兩種情況:漏標(biāo)和錯(cuò)標(biāo)。

漏標(biāo)

原本不是垃圾,但是GC的過程中,用戶線程將其引用關(guān)系修改,導(dǎo)致GC Roots不可達(dá),成為了垃圾。這種情況還好一點(diǎn),無非就是產(chǎn)生了一些浮動(dòng)垃圾,下次GC再清理就好了。

錯(cuò)標(biāo)

原本是垃圾,但是GC的過程中,用戶線程將引用重新指向了它,這時(shí)如果GC一旦將其回收,將會(huì)導(dǎo)致程序運(yùn)行錯(cuò)誤。

三色標(biāo)記算法

  為什么CMS的GC線程可以和用戶線程一起工作 ? 采用三色標(biāo)記解決

GC過程中需要stop the world的原因是什么

標(biāo)記的過程大致如下:

  1. 剛開始,所有的對(duì)象都是白色,沒有被訪問。

  2. 將GC Roots直接關(guān)聯(lián)的對(duì)象置為灰色。

  3. 遍歷灰色對(duì)象的所有引用,灰色對(duì)象本身置為黑色,引用置為灰色。

  4. 重復(fù)步驟3,直到?jīng)]有灰色對(duì)象為止。

  5. 結(jié)束時(shí),黑色對(duì)象存活,白色對(duì)象回收。

這個(gè)過程正確執(zhí)行的前提是沒有其他線程改變對(duì)象間的引用關(guān)系,然而,并發(fā)標(biāo)記的過程中,用戶線程仍在運(yùn)行,因此就會(huì)產(chǎn)生漏標(biāo)和錯(cuò)標(biāo)的情況。

漏標(biāo)

假設(shè)GC已經(jīng)在遍歷對(duì)象B了,而此時(shí)用戶線程執(zhí)行了A.B=null的操作,切斷了A到B的引用。

GC過程中需要stop the world的原因是什么

本來執(zhí)行了A.B=null之后,B、D、E都可以被回收了,但是由于B已經(jīng)變?yōu)榛疑?,它仍?huì)被當(dāng)做存活對(duì)象,繼續(xù)遍歷下去。
最終的結(jié)果就是本輪GC不會(huì)回收B、D、E,留到下次GC時(shí)回收,也算是浮動(dòng)垃圾的一部分。

實(shí)際上,這個(gè)問題依然可以通過「寫屏障」來解決,只要在A寫B(tài)的時(shí)候加入寫屏障,記錄下B被切斷的記錄,重新標(biāo)記時(shí)可以再把他們標(biāo)為白色即可。

錯(cuò)標(biāo)

假設(shè)GC線程已經(jīng)遍歷到B了,此時(shí)用戶線程執(zhí)行了以下操作:

B.D=null;//B到D的引用被切斷A.xx=D;//A到D的引用被建立

這種情況什么時(shí)候成立? 一個(gè)對(duì)象失去了引用,還可以再被其它對(duì)象引用?

他這個(gè)的是可達(dá)性分析的時(shí)候,從 B.D = null , 但是 別的線程里面還用著 D , 所以還能進(jìn)行引用D 

GC過程中需要stop the world的原因是什么

B到D的引用被切斷,且A到D的引用被建立。

此時(shí)GC線程繼續(xù)工作,由于B不再引用D了,盡管A又引用了D,但是因?yàn)锳已經(jīng)標(biāo)記為黑色,GC不會(huì)再遍歷A了,所以D會(huì)被標(biāo)記為白色,最后被當(dāng)做垃圾回收。
可以看到錯(cuò)標(biāo)的結(jié)果比漏表嚴(yán)重的多,浮動(dòng)垃圾可以下次GC清理,而把不該回收的對(duì)象回收掉,將會(huì)造成程序運(yùn)行錯(cuò)誤。
 

錯(cuò)標(biāo)只有在滿足下面兩種情況下才會(huì)發(fā)生:

GC過程中需要stop the world的原因是什么

只要打破任一條件,就可以解決錯(cuò)標(biāo)的問題。

原始快照和增量更新

原始快照打破的是第一個(gè)條件:當(dāng)灰色對(duì)象指向白色對(duì)象的引用被斷開時(shí),就將這條引用關(guān)系記錄下來。當(dāng)掃描結(jié)束后,再以這些灰色對(duì)象為根,重新掃描一次。相當(dāng)于無論引用關(guān)系是否刪除,都會(huì)按照剛開始掃描時(shí)那一瞬間的對(duì)象圖快照來掃描。

增量更新打破的是第二個(gè)條件:當(dāng)黑色指向白色的引用被建立時(shí),就將這個(gè)新的引用關(guān)系記錄下來,等掃描結(jié)束后,再以這些記錄中的黑色對(duì)象為根,重新掃描一次。相當(dāng)于黑色對(duì)象一旦建立了指向白色對(duì)象的引用,就會(huì)變?yōu)榛疑珜?duì)象。

寫屏障

這個(gè)寫屏障指的可不是并發(fā)編程里的寫屏障哦!這里的寫屏障指的是屬性賦值的前后加入一些處理,類似于AOP。

CMS采用的方案就是:寫屏障+增量更新來實(shí)現(xiàn)的,打破的是第二個(gè)條件。

當(dāng)黑色指向白色的引用被建立時(shí),通過寫屏障來記錄引用關(guān)系,等掃描結(jié)束后,再以引用關(guān)系里的黑色對(duì)象為根重新掃描一次即可。

GC時(shí)為什么要暫停用戶線程?

有沒有辦法,徹底消除STW ?  看樣子是重新標(biāo)記的時(shí)候,也避免不了標(biāo)錯(cuò),所以才必須 STW

                   |- 黑色指向白色的引用被建立            增量更新     |
錯(cuò)標(biāo)-->      |                                                                              | --> 寫屏障
                   |- 色指向白色的引用全部被破壞        原始快照     |


弱三色不變式:所有被黑色對(duì)象引用的白色對(duì)象都處于灰色保護(hù)狀態(tài)。                            
強(qiáng)三色不變式:不存在從黑色對(duì)象執(zhí)行白色對(duì)象的指針。

基于原始快照的解決方案:弱三色不變式,將A的目標(biāo)引用對(duì)象標(biāo)位灰色。
基于增量更新的解決方案:強(qiáng)三色不變式,將A的目標(biāo)引用對(duì)象標(biāo)位黑色。

CMS的缺點(diǎn)

盡管CMS是一款里程碑式的垃圾收集器,開啟了GC線程和用戶線程同時(shí)工作的先河,但是不管是哪個(gè)JDK版本,CMS從來都不是默認(rèn)的垃圾收集器,究其原因,還是因?yàn)镃MS不太完美,存在一些缺點(diǎn)。

CMS碎片化問題,沒有可用空間時(shí),用SeralOld單線程整理空間,為什么不用Parallel Old ?

GC過程中需要stop the world的原因是什么

垃圾回收器集

串行收集器:serial + serial old

并行收集器:Paraller Scanvenge + Paraller Old

并發(fā)標(biāo)記清除收集器組合:ParNew + CMS + serial old

G1 垃圾回收器

Java 7 update4 之后引入,Jdk9默認(rèn)

邏輯分代,物理不分代

G1 將Java 堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域,JVM最多可以有2048個(gè)Region。一般Region大小等于堆大小除以2048。 比如堆大小為4096M,則Region大小為2M

GC過程中需要stop the world的原因是什么

G1收集器的運(yùn)行過程:

  • 初始標(biāo)記(Initial Marking): 標(biāo)記GC Roots 能直接關(guān)聯(lián)到的對(duì)象,并且修改TAMS指針的值,讓下一階段用戶線程并發(fā)運(yùn)行時(shí),能正確在可用的Region中分配新對(duì)象,需要耗時(shí)較短的停頓線程,但是是借用Minor GC的時(shí)候同步完成的,所以在這個(gè)階段實(shí)際沒有額外的停頓

  • 并發(fā)標(biāo)記(Concurrent Marking): 從GC Roots 開始對(duì)堆中對(duì)象進(jìn)行可達(dá)性分析,遞歸掃描整個(gè)堆里面的對(duì)象圖,找出要回收的對(duì)象,這個(gè)階段耗時(shí)較長,但可以和用戶程序并發(fā)執(zhí)行。

  • 最終標(biāo)記(Final Marking): 對(duì)用戶線程做另一個(gè)短暫的暫停,用戶處理并發(fā)階段結(jié)束后仍遺留下來的最后那少量的SATB記錄

  • 篩選回收(Live Data Counting and Evacuation): 負(fù)責(zé)更新Region的統(tǒng)計(jì)數(shù)據(jù),對(duì)各個(gè)Region的回收價(jià)值和成本進(jìn)行排序,根據(jù)用戶鎖期望的停頓時(shí)間來制定回收計(jì)劃,可以只有選擇任意多個(gè)Region構(gòu)成回收集,然后把決定回收的那一部分Region的存活對(duì)象賦值到空的Region中,再清理整個(gè)Region的全部空間。

根據(jù)期望的停頓時(shí)間,進(jìn)行預(yù)估比例的回收,可能到時(shí)回收跟不上對(duì)象的分配。進(jìn)而觸發(fā)Full GC

一張圖讓你看懂JVM之垃圾回收算法詳解

GC過程中需要stop the world的原因是什么

到此,相信大家對(duì)“GC過程中需要stop the world的原因是什么”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI