Java 垃圾回收(GC)機(jī)制

一、為什么要進(jìn)行垃圾回收

隨著程序的運(yùn)行,內(nèi)存中存在的實(shí)例對(duì)象栏饮、變量等信息占據(jù)的內(nèi)存越來(lái)越多,其中有很多對(duì)象再也用不到磷仰,這些用不到的對(duì)象就被稱之為垃圾袍嬉,如果不及時(shí)進(jìn)行垃圾回收,必然會(huì)帶來(lái)程序性能的下降灶平,甚至?xí)驗(yàn)榭捎脙?nèi)存不足造成一些不必要的系統(tǒng)異常伺通。

垃圾回收機(jī)制主要是對(duì) JVM 中堆內(nèi)存進(jìn)行管理,如果對(duì) JVM 相關(guān)的概念還不了解逢享,可以看一看《JVM 從入門到出門》這篇文章罐监。

二、如何判定對(duì)象是否為垃圾

1瞒爬、引用計(jì)數(shù)法
給對(duì)象添加一引用計(jì)數(shù)器弓柱,被引用一次計(jì)數(shù)器值就加 1;當(dāng)引用失效時(shí)疮鲫,計(jì)數(shù)器值就減 1吆你;計(jì)數(shù)器為 0 時(shí),對(duì)象就是垃圾俊犯。

優(yōu)點(diǎn)是執(zhí)行效率高妇多,缺點(diǎn)是無(wú)法解決對(duì)象之間相互循環(huán)引用的問(wèn)題鲸郊。

2啊研、可達(dá)性分析算法
以 GC Roots 為起始點(diǎn)進(jìn)行搜索,判斷對(duì)象的引用鏈?zhǔn)欠窨蛇_(dá)屁商,可達(dá)的對(duì)象都是存活的绢彤,不可達(dá)的對(duì)象可被回收七问。GC Roots 一般包含以下內(nèi)容:

  • 虛擬機(jī)棧中局部變量表中引用的對(duì)象
  • 本地方法棧中 JNI 中引用的對(duì)象
  • 方法區(qū)中類靜態(tài)屬性引用的對(duì)象
  • 方法區(qū)中的常量引用的對(duì)象

對(duì)象死亡(被回收)前的最后一次掙扎:
即使在可達(dá)性分析算法中不可達(dá)的對(duì)象,也并非是“必死不可”茫舶,這時(shí)候它們暫時(shí)處于“緩刑”階段械巡,要真正宣告一個(gè)對(duì)象死亡,至少要經(jīng)歷兩次標(biāo)記過(guò)程饶氏。
第一次標(biāo)記:如果對(duì)象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒(méi)有與 GC Roots 相連接的引用鏈讥耗,那它將會(huì)被第一次標(biāo)記;
第二次標(biāo)記:在第一次標(biāo)記后接著會(huì)進(jìn)行一次篩選疹启,篩選的條件是此對(duì)象是否有必要執(zhí)行 finalize() 方法古程。在 finalize() 方法中沒(méi)有重新與引用鏈建立關(guān)聯(lián)關(guān)系的,將被進(jìn)行第二次標(biāo)記喊崖。
第二次標(biāo)記成功的對(duì)象將真的會(huì)被回收挣磨,如果對(duì)象在 finalize() 方法中重新與引用鏈建立了關(guān)聯(lián)關(guān)系雇逞,那么將會(huì)逃離本次回收,繼續(xù)存活茁裙。

三塘砸、回收垃圾的算法

回收垃圾的算法主要有 4 種:標(biāo)記清除算法, 標(biāo)記整理算法呜达,復(fù)制算法谣蠢,分代收集算法。下面分別介紹查近。

1、標(biāo)記清除

標(biāo)記:從 GC Roots 為起始點(diǎn)進(jìn)行掃描挤忙,如果是活動(dòng)對(duì)象霜威,則程序會(huì)在對(duì)象頭部打上標(biāo)記。
清除:對(duì)堆內(nèi)存從頭到尾進(jìn)行線性遍歷册烈,回收不可達(dá)對(duì)象戈泼。

標(biāo)記清除

但是,標(biāo)記清除算法會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片赏僧,導(dǎo)致無(wú)法給大對(duì)象分配內(nèi)存大猛。例如上圖中 B 與 E 之間只剩 2 格,若有一個(gè)新對(duì)象要占用 3 格淀零,則需要開辟另外的內(nèi)存或者 Full GC挽绩。

2、標(biāo)記整理

標(biāo)記:從 GC Roots 為起始點(diǎn)進(jìn)行掃描驾中,如果是活動(dòng)對(duì)象唉堪,則程序會(huì)在對(duì)象頭部打上標(biāo)記。
整理:移動(dòng)所有存活對(duì)象肩民,且按照內(nèi)存地址次序依次排列唠亚,然后將末端以后的內(nèi)存地址全部回收。

標(biāo)記整理

彌補(bǔ)了標(biāo)記清除算法的不足持痰,不會(huì)產(chǎn)生內(nèi)存碎片灶搜。但是需要移動(dòng)大量對(duì)象,處理效率比較低工窍。

3割卖、復(fù)制算法

將內(nèi)存劃分為大小相等的兩塊,每次只使用其中一塊移剪,當(dāng)這一塊內(nèi)存用完了就將還存活的對(duì)象復(fù)制到另一塊上面究珊,然后再把使用過(guò)的內(nèi)存空間進(jìn)行一次清理。

復(fù)制算法

不會(huì)產(chǎn)生內(nèi)存碎片問(wèn)題纵苛,順序分配內(nèi)存剿涮,執(zhí)行效率高言津,但每次只使用了一半的內(nèi)存,未免有點(diǎn)浪費(fèi)取试。

4悬槽、分代收集

分代收集實(shí)際上就是將上述 3 種算法綜合起來(lái),針對(duì)不同的區(qū)域瞬浓,采用不同的方法初婆,按照對(duì)象的生命周期的不同劃分區(qū)域,采用不同的垃圾回收算法猿棉,以提高 JVM 回收效率磅叛。

Java 堆分為兩部分,Java 堆 = 新生代 + 老年代萨赁,默認(rèn)分別占堆空間為 1/3弊琴、2/3;其中杖爽,新生代 = Eden + From Survivor + To Survivor敲董,默認(rèn)為 8:1:1。這樣劃分是由于對(duì)象生存周期的特殊性慰安,針對(duì)不同的對(duì)象腋寨,采用不同的方法。

Java 堆內(nèi)存劃分

新生代使用:復(fù)制算法
老年代使用:標(biāo)記清除 或 標(biāo)記整理 算法

所有的對(duì)象都在 Eden 區(qū)創(chuàng)建化焕,由于大部分對(duì)象都是“朝生夕滅”萄窜,只有少量對(duì)象能存活下來(lái),所以在新生代采用復(fù)制算法锣杂,只有少量對(duì)象需要復(fù)制脂倦,這樣最劃算。

當(dāng) Eden 區(qū)滿了元莫,那么就會(huì)觸發(fā)一次 Young GC赖阻,也就是年輕代垃圾回收。少量有用的對(duì)象會(huì)復(fù)制到 From 區(qū)踱蠢。這樣整個(gè)Eden區(qū)就被清理干凈了火欧,可以繼續(xù)創(chuàng)建新的對(duì)象。

當(dāng) Eden 區(qū)再次被用完茎截,就再觸發(fā)一次 YoungGC苇侵,這個(gè)時(shí)候跟剛才稍稍有點(diǎn)區(qū)別。這次觸發(fā) Young GC 后企锌,會(huì)將 Eden 區(qū)與 From 區(qū)還在被使用的對(duì)象復(fù)制到 To 區(qū)榆浓,再下一次 YoungGC 的時(shí)候,則是將 Eden 區(qū)與 To 區(qū)中的還在被使用的對(duì)象復(fù)制到 From 區(qū)撕攒。

經(jīng)過(guò)若干次 YoungGC 后陡鹃,有些對(duì)象在 From 與 To 之間來(lái)回游蕩烘浦,這時(shí)候 From 區(qū)與 To 區(qū)亮出了底線(閾值),這些家伙要是到現(xiàn)在還沒(méi)掛掉萍鲸,對(duì)不起闷叉,一起復(fù)制老年代吧。

而在老年代脊阴,大部分對(duì)象任然會(huì)繼續(xù)存活下來(lái)握侧,此時(shí)采用標(biāo)記整理或者標(biāo)記清除算法,這樣最劃算嘿期。

對(duì)象如何晉升到老年代品擎?
1、經(jīng)歷一定次數(shù)的 Minor GC 任然存活的對(duì)象秽五,默認(rèn) 15 次孽查;
2、Eden 區(qū)或 Survivor 區(qū)域存放不下的對(duì)象坦喘;
3、新生成的大對(duì)象西设,直接放入老年代瓣铣。

四、常見的垃圾收集器

Serial 垃圾收集器(單線程贷揽,復(fù)制算法):

Serial 是單線程收集棠笑,進(jìn)行垃圾收集時(shí)必須暫停所有工作線程。但是它簡(jiǎn)單高效禽绪,JVM Client 模式下默認(rèn)的年輕代收集器蓖救。

串行收集器

ParNew 垃圾收集器(多線程,復(fù)制算法):

ParNew 是多線程收集器印屁,是 CMS 默認(rèn)的新生代垃圾回收器循捺,其他行為特點(diǎn)與 Serial 一樣。

Parallel Scavenge 垃圾收集器(多線程雄人,復(fù)制算法):

Parallel Scavenge 和 ParNew 一樣从橘,都是多線程、新生代垃圾收集器础钠。兩者的區(qū)別在于:
Parallel Scavenge 追求 CPU 吞吐量恰力,能夠在較短時(shí)間內(nèi)完成指定任務(wù),因此適合沒(méi)有交互的后臺(tái)計(jì)算旗吁;
ParNew 追求降低用戶停頓時(shí)間踩萎,適合交互式應(yīng)用。

Serial Old 垃圾收集器(單線程很钓,標(biāo)記整理算法):

Serial Old 收集器是 Serial 的老年代版本香府,都是單線程收集器董栽,都適合客戶端應(yīng)用。它們唯一的區(qū)別就是:Serial Old 工作在老年代回还,使用“標(biāo)記-整理”算法裆泳;Serial 工作在新生代,使用“復(fù)制”算法柠硕。

CMS 垃圾收集器(標(biāo)記清楚算法):

CMS (Concurrent Mark Sweep工禾,并發(fā)標(biāo)記清除) 收集器是以獲取最短回收停頓時(shí)間為目標(biāo)的收集器(追求低停頓),它在垃圾收集時(shí)蝗柔,用戶線程 和 GC 線程并發(fā)執(zhí)行闻葵,因此在垃圾收集過(guò)程中不會(huì)感到明顯的卡頓。

具體執(zhí)行過(guò)程如下圖:初始標(biāo)記 (Initial Mark) —> 并發(fā)標(biāo)記 (Concurrent Mark) —> 重新標(biāo)記 (Remark) —> 并發(fā)清除 (Concurrnet Sweep)癣丧。

并發(fā)執(zhí)行
  1. 初始標(biāo)記 (Initial Mark):僅僅只是標(biāo)記一下 GC Roots 能直接關(guān)聯(lián)到的對(duì)象槽畔,速度很快,需要 Stop The World胁编。

  2. 并發(fā)標(biāo)記 (Concurrent Mark):從 GC Roots 的直接關(guān)聯(lián)對(duì)象開始遍歷整個(gè)對(duì)象圖的過(guò)程厢钧,耗時(shí)較長(zhǎng),但不需要停頓用戶線程嬉橙,可與垃圾收集器線程一起并發(fā)執(zhí)行早直。

  3. 重新標(biāo)記 (Remark):該階段是為了修正并發(fā)標(biāo)記期間,因用戶程序運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄市框,這個(gè)階段需要 Stop The World霞扬,而且停頓時(shí)間通常比初始階段稍長(zhǎng)一些,但也遠(yuǎn)比并發(fā)標(biāo)記階段的時(shí)間短枫振。

  4. 并發(fā)清除 (Concurrnet Sweep):清理刪除掉標(biāo)記階段判斷已經(jīng)死亡的對(duì)象喻圃,由于不需要移動(dòng)存活對(duì)象,所有這個(gè)階段可以和用戶線程并發(fā)執(zhí)行粪滤。

CMS 收集器是并發(fā)收集斧拍,有兩次 Stop The Words,兩次標(biāo)記额衙,因?yàn)?GC 線程和應(yīng)用線程同時(shí)執(zhí)行饮焦,好比你媽在打掃房間,你還在扔紙屑窍侧,可能產(chǎn)生新的引用關(guān)系县踢。

CMS 的缺點(diǎn):吞吐量低,無(wú)法處理浮動(dòng)垃圾伟件,導(dǎo)致頻繁 Full GC硼啤,使用“標(biāo)記-清除”算法產(chǎn)生碎片空間。

G1 垃圾收集器

G1 (Garbage-First) 是一款面向服務(wù)端應(yīng)用的垃圾收集器斧账,它弱化了新生代和老年代的概念谴返,雖然還保留了新生代和來(lái)年代的概念煞肾,但新生代和老年代不再是物理隔離的了,而是將堆劃分為一塊塊獨(dú)立的 Region(默認(rèn)將整堆劃分為 2048 個(gè) Region)嗓袱,每個(gè) Region 也不會(huì)確定地為某個(gè)代服務(wù)籍救,可以按需在年輕代和老年代之間切換。

G1 GC內(nèi)存布局

內(nèi)存的回收是以 Region 為基本單位的渠抹,當(dāng)要進(jìn)行垃圾收集時(shí)蝙昙,首先估計(jì)每個(gè) Region 中垃圾的數(shù)量,每次都從垃圾回收價(jià)值最大的 Region 開始回收梧却,因此可以獲得最大的回收效率奇颠。

什么是回收價(jià)值最大?
價(jià)值最大就是回收耗時(shí)最短放航,回收時(shí)間就是復(fù)制的時(shí)間烈拒,存活對(duì)象越多,回收時(shí)要復(fù)制的間就越長(zhǎng)广鳍,回收的效益就越低荆几,例如同等大小的兩個(gè) Region,回收一個(gè)需要 100ms赊时,另一個(gè)需要 50ms伴郁,那么 G1 肯定優(yōu)先回收 50ms 的,這樣就保證了在有效時(shí)間內(nèi)能回收更多的堆空間蛋叼。

每個(gè) Region 都有一個(gè) Remembered Set,用來(lái)記錄該 Region 對(duì)象的引用對(duì)象所在的 Region剂陡。通過(guò)使用 Remembered Set狈涮,在做可達(dá)性分析的時(shí)候就可以避免全堆掃描。

從整體上看鸭栖, G1 是基于“標(biāo)記-整理”算法實(shí)現(xiàn)的收集器歌馍,從局部(兩個(gè) Region 之間)上看是基于“復(fù)制”算法實(shí)現(xiàn)的,這意味著運(yùn)行期間不會(huì)產(chǎn)生內(nèi)存空間碎片晕鹊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末松却,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子溅话,更是在濱河造成了極大的恐慌晓锻,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件飞几,死亡現(xiàn)場(chǎng)離奇詭異砚哆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)屑墨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門躁锁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)纷铣,“玉大人,你說(shuō)我怎么就攤上這事战转∷蚜ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵槐秧,是天一觀的道長(zhǎng)啄踊。 經(jīng)常有香客問(wèn)我,道長(zhǎng)色鸳,這世上最難降的妖魔是什么社痛? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮命雀,結(jié)果婚禮上蒜哀,老公的妹妹穿的比我還像新娘。我一直安慰自己吏砂,他們只是感情好撵儿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狐血,像睡著了一般淀歇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匈织,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天浪默,我揣著相機(jī)與錄音,去河邊找鬼缀匕。 笑死纳决,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的乡小。 我是一名探鬼主播阔加,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼满钟!你這毒婦竟也來(lái)了胜榔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤湃番,失蹤者是張志新(化名)和其女友劉穎夭织,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牵辣,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摔癣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片择浊。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡戴卜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出琢岩,到底是詐尸還是另有隱情投剥,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布担孔,位于F島的核電站江锨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏糕篇。R本人自食惡果不足惜啄育,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拌消。 院中可真熱鬧挑豌,春花似錦、人聲如沸墩崩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鹦筹。三九已至铝阐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铐拐,已是汗流浹背徘键。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遍蟋,地道東北人啊鸭。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像匿值,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赂摆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容