# 前言
在 深入淺出 JVM GC(1) 中朦前,限于上篇文章的篇幅唬格,我們留下了一個(gè)問(wèn)題 : 如何回收? 這篇文章將重點(diǎn)講述這個(gè)問(wèn)題仓坞。
在上篇文章中背零,我們也列出了一些大綱,今天我們就按照那個(gè)大綱來(lái)逐個(gè)講解无埃。在此徙瓶,我將大綱復(fù)制過(guò)來(lái)毛雇。
垃圾回收算法
- 標(biāo)記清除算法
- 復(fù)制算法
- 標(biāo)記整理算法
- 分代收集算法(堆如何分代)
有哪些垃圾收集器
- Serial 串行收集器(只適用于堆內(nèi)存256m 以下的 JVM )
- ParNew 并行收集器(Serial 收集器的多線程版本)
- Parallel Scavenge (PS 收集器,該收集器以吞吐量為主要目的侦镇,是1.8的默認(rèn) GC)
- CMS 收集器(該收集器全稱 Concurrent Mark Sweep灵疮,是一種關(guān)注最短停頓時(shí)間的垃圾收集器)
- G1 收集器(JDK 9 的默認(rèn) GC)
有哪些GC
- Young GC(又稱 YGC,minor GC壳繁,年輕代 GC)
- Old GC (老年代 GC震捣,只有 CMS 才會(huì)單獨(dú)回收 Old 區(qū))
- Full GC(又稱 major GC)
- Mixed GC(混合 GC,G1 收集器獨(dú)有)
1. 有哪些垃圾回收算法
- 標(biāo)記清除算法
- 復(fù)制算法
- 標(biāo)記整理算法
- 分代收集算法(堆如何分代)
1. 標(biāo)記清除算法
GC 中最基礎(chǔ)的算法就是標(biāo)記-清除算法闹炉,所謂標(biāo)記清除蒿赢,就是通過(guò)可達(dá)性分析,標(biāo)記哪些是垃圾對(duì)象剩胁,然后清除诉植。之所以說(shuō)是最簡(jiǎn)單的算法,是因?yàn)楹竺娴膸追N算法都是基于它的昵观。
但是這個(gè)算法有2個(gè)不足之處:1. 碎片問(wèn)題晾腔,標(biāo)記清除之后會(huì)導(dǎo)致大量?jī)?nèi)存不連續(xù)的碎片,空間碎片太多會(huì)導(dǎo)致分配大對(duì)象時(shí)無(wú)法找到足夠的連續(xù)內(nèi)存從而提前觸發(fā) Full GC (此 GC 嚴(yán)重影響應(yīng)用性能)啊犬。2. 效率問(wèn)題灼擂,標(biāo)記和清除這兩個(gè)過(guò)程的效率都不高。我們通過(guò)一幅圖來(lái)看看標(biāo)記清除的算法:
可以從上圖看出觉至,回收后剔应,出現(xiàn)了大量的內(nèi)存不連續(xù)的內(nèi)存塊。
2. 復(fù)制算法
為了解決效率問(wèn)題语御,人們發(fā)明了一種復(fù)制算法(Coping)峻贮。它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊应闯。當(dāng)這一塊的用完了纤控,就開(kāi)始垃圾回收,將有用的對(duì)象復(fù)制到另一個(gè)空閑的內(nèi)存上碉纺,清空之前使用的內(nèi)存塊船万。這樣使得每次都對(duì)整個(gè)半?yún)^(qū)回收,而且也不用考慮內(nèi)存碎片問(wèn)題骨田,只需要移動(dòng)堆頂指針耿导,按順序分配即可,實(shí)現(xiàn)簡(jiǎn)單态贤,運(yùn)行高效舱呻。
但是凡事都是有缺點(diǎn)的,復(fù)制算法的缺點(diǎn)就是內(nèi)存縮小到了原來(lái)的一半抵卫,無(wú)法充分利用內(nèi)存空間狮荔。
總是有取舍的胎撇。
現(xiàn)代的所有商業(yè)虛擬機(jī)都是采用這種算法來(lái)回收新生代介粘≈呈希基于統(tǒng)計(jì)學(xué),人們得出99% 的對(duì)象都是朝生夕死的姻采,所以不需要留出那么大的空間保存存活的對(duì)象雅采,也就是不要1:1 的比例來(lái)劃分內(nèi)存。
通常的做法是:
將內(nèi)存分為一個(gè)較大的Eden(伊甸園)空間和兩塊較小的 Survivor(幸存區(qū))空間慨亲,每次使用 Eden 和其中一塊 Survivor 婚瓜,當(dāng)回收時(shí),將 Eden 和 Survivor 還存活著的對(duì)象一次性的復(fù)制到另外一塊 Survivor 空間刑棵,最后清理掉 Eden 和剛剛使用的 Survivor 空間巴刻。Hotspot 默認(rèn)的比例是 8:1:1,也就是說(shuō)蛉签,每次新生代可用內(nèi)存空間為新生代總空間的90%胡陪,只有10%的內(nèi)存會(huì)被浪費(fèi),從一定程度上解決了復(fù)制算法浪費(fèi)空間的問(wèn)題碍舍。
當(dāng)然柠座,98% 的對(duì)象可回收只是一般的情況下,我們無(wú)法保證每次回收都只有不多于 10% 的對(duì)象存活片橡,當(dāng) Survivor 空間不夠用時(shí)怎么辦呢妈经?肯定需要依賴其他內(nèi)存(老年代)進(jìn)行所謂的分配擔(dān)保(Handle Promotion)。
什么是分配擔(dān)保呢捧书?
如果另外一塊 Survivor 區(qū)域沒(méi)有足夠空間存放上一次新生代手機(jī)下來(lái)的存活對(duì)象時(shí)吹泡,這些對(duì)象將直接通過(guò)分配擔(dān)保機(jī)制進(jìn)入老年代。當(dāng)然经瓷,具體細(xì)節(jié)這句話無(wú)法詳細(xì)說(shuō)明爆哑,我們將會(huì)在之后闡述具體細(xì)節(jié)。
3. 標(biāo)記整理算法
從上面我們可以看出了嚎,復(fù)制算法的效率很高泪漂,請(qǐng)注意,該算法只有在對(duì)象存活率較低的時(shí)候(98% 對(duì)象可被回收)才能體現(xiàn)出效率歪泳。而如果一次 GC 活動(dòng)之后萝勤,存活對(duì)象很多,那么就需要復(fù)制大量的對(duì)象呐伞,很明顯敌卓,會(huì)導(dǎo)致效率不高;更關(guān)鍵的是伶氢,還需要額外的空間進(jìn)行分配擔(dān)保趟径。
所以瘪吏,存活對(duì)象時(shí)間很長(zhǎng)的老年代一般不使用該算法。
根據(jù)老年代的特點(diǎn)蜗巧,一般使用“標(biāo)記-整理(Mark-Compact)”算法掌眠,標(biāo)記過(guò)程仍然與 “標(biāo)記清除” 算法一樣,但我們知道幕屹,標(biāo)記清除算法會(huì)產(chǎn)生大量的內(nèi)存碎片蓝丙,對(duì)性能影響很大,所以標(biāo)記整理算法后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理望拖,而是讓所有存活的對(duì)象像一個(gè)方向移動(dòng)渺尘,然后清理掉邊界之外的內(nèi)存。也就是將那些原來(lái)散落的對(duì)象移動(dòng)在一起说敏,讓碎片不再存在鸥跟。
可以說(shuō),標(biāo)記整理算法相對(duì)于標(biāo)記清除算法犧牲了一些性能盔沫,但卻避免了內(nèi)存碎片的產(chǎn)生医咨,在大部分場(chǎng)合,可抵消掉整理過(guò)程中產(chǎn)生的性能損耗迅诬。
4. 分代收集算法
上面我們提到了幾個(gè)名詞腋逆,新生代,老年代侈贷,這些就是分代算法中名詞惩歉。分代算法最主要的就是根據(jù)對(duì)象存活周期的不同將內(nèi)存分成幾塊,一般是把 Java 堆分成新生代和老年代俏蛮,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/p>
在新生代中撑蚌,每次垃圾收集都發(fā)現(xiàn)有大批對(duì)象死去,只有少量存活搏屑,那就選用復(fù)制算法争涌,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集。而老年代中因?yàn)閷?duì)象存活率高辣恋,沒(méi)有額外空間對(duì)它進(jìn)行分配擔(dān)保亮垫,就必須使用“標(biāo)記清理” 或者 “標(biāo)記整理” 算法來(lái)進(jìn)行回收。
2. 有哪些垃圾收集器
上面說(shuō)的這些算法都是實(shí)現(xiàn)垃圾收集器的基礎(chǔ)伟骨。
如果說(shuō)收集算法是內(nèi)存回收的方法論饮潦,那么垃圾收集器就是內(nèi)存回收的具體實(shí)現(xiàn)。
Hotspot 虛擬機(jī)所包含的所有收集器如下圖:
從上圖中看到携狭,一共有6種 GC 組合(忽略 G1 和 為CMS備份的 SerialOld 組合 )继蜡。
- Serial + Serial Old
- Serial + CMS
- ParNew + CMS
- ParNew + Serial Old
- Parallel Scavenge + Serial Old
- Parallel Scavenge + Parallel Old
大家看到這里,一定有個(gè)疑問(wèn),為什么需要這么多垃圾收集器稀并?
答案是:沒(méi)有任何一種垃圾收集器是完美的仅颇,沒(méi)有任何一種垃圾收集器適合所有的應(yīng)用情況。
每個(gè)應(yīng)用都需要自己的特定垃圾收集器碘举,因此忘瓦,可以說(shuō),GC 調(diào)優(yōu)是門(mén)藝術(shù)殴俱,沒(méi)有放之四海皆準(zhǔn)的 GC政冻。需要工程師們?nèi)ジ鶕?jù)應(yīng)用的特性不斷調(diào)優(yōu)枚抵。
這么多 GC 线欲,限于篇幅,我們將在 深入淺出 JVM GC(3) 中慢慢解釋汽摹。
這里只是列出一個(gè)大綱李丰。
接下來(lái)我們將說(shuō)說(shuō)關(guān)于 GC 的一些概念,方便閱讀后面的關(guān)于 GC 處理器的文章逼泣。
3. 有哪些GC
- Young GC(又稱 YGC趴泌,minor GC,年輕代 GC)
- Old GC (老年代 GC拉庶,只有 CMS 才會(huì)單獨(dú)回收 Old 區(qū))
- Full GC(又稱 major GC)
- Mixed GC(混合 GC嗜憔,G1 收集器獨(dú)有)
關(guān)于這些 GC 的分類,R 大一個(gè)回答比較清楚:Major GC和Full GC的區(qū)別是什么氏仗?觸發(fā)條件呢吉捶?
從大的方面講,GC 只分為兩種皆尔,一種是不收集整個(gè)堆呐舔,一種是收集整個(gè)堆。
Partial GC:并不收集整個(gè)GC堆的模式
- Young GC:只收集young gen的GC
- Old GC:只收集old gen的GC慷蠕。只有CMS的concurrent collection是這個(gè)模式
- Mixed GC:收集整個(gè)young gen以及部分old gen的GC珊拼。只有G1有這個(gè)模式
Full GC:收集整個(gè)堆,包括young gen流炕、old gen澎现、perm gen(如果存在的話)等所有部分的模式。
1. YGC
YGC 又稱 Young GC 每辟,minor GC 剑辫,年輕代 GC。顧名思義影兽,該 GC 過(guò)程發(fā)生在年輕代中揭斧。從分代算法中,我們知道,JVM 為了性能考慮讹开,通常將內(nèi)存區(qū)域根據(jù)對(duì)象生命周期的不同分為年輕代和年老代盅视。
新創(chuàng)建的對(duì)象基本上都存放在年輕代(除了一些大對(duì)象),因?yàn)榇蠖鄶?shù)對(duì)象都是很快變成引用不可達(dá)旦万,所以大多數(shù)對(duì)象都在年輕代中創(chuàng)建闹击,然后消失。當(dāng)對(duì)象從這塊內(nèi)存區(qū)域消失時(shí)成艘,我們稱之為 YGC赏半。
什么時(shí)候發(fā)生 YGC 呢?當(dāng) Eden 不夠放入新創(chuàng)建的對(duì)象時(shí)淆两,也就是Eden 區(qū)滿了断箫,JVM 就會(huì)清理Eden 區(qū)的空間,將存活的對(duì)象放入 to 區(qū)秋冰,如果 to 區(qū)放不下仲义,則直接進(jìn)入老年代。如果 to 區(qū)能放下剑勾,則放入 to 區(qū)埃撵,然后清理掉無(wú)用對(duì)象,第二次 YGC 時(shí)虽另,GC 掃描 Eden 區(qū)和 to 區(qū)暂刘,將這兩個(gè)區(qū)的存活對(duì)象放入到 from 區(qū),將 to 區(qū)清空(總之一定會(huì)保證有一個(gè) Survivor 區(qū)是干凈的)捂刺,同樣的谣拣,如果 from 區(qū)放不下,則通過(guò)分配擔(dān)保機(jī)制進(jìn)入老年代叠萍。如果 YGC 后芝发,仍放不下新對(duì)象,則也通過(guò)分配擔(dān)保進(jìn)入老年代苛谷。
2. Old GC
通常辅鲸,我們將 Old GC 等同于 Full GC,為什么呢腹殿?我們?cè)敿?xì)解釋一下独悴。
什么時(shí)候發(fā)生 Old GC? 當(dāng)老年代空間滿了的時(shí)候锣尉。也就是說(shuō)通常是 YGC 后有很多對(duì)象進(jìn)入到老年代刻炒,而老年代無(wú)法放下這些對(duì)象,這時(shí)候就需要對(duì)老年代 GC自沧。而通常的 Old GC 其實(shí)就是 Full GC 坟奥。
3. Full GC
也就是全 GC 树瞭,對(duì)整個(gè)堆和方法區(qū)(如果存在)進(jìn)行 GC。
哪些情況會(huì) Full GC 呢爱谁?
- System.gc() 方法的調(diào)用
- heap dump 帶 GC
- 永久代(方法區(qū))空間不夠
- 當(dāng)準(zhǔn)備出發(fā) YGC 時(shí)晒喷,發(fā)現(xiàn)之前 YGC 后晉升對(duì)象的大小比目前 Old 區(qū)的剩余空間大,則不會(huì)觸發(fā) YGC 访敌,轉(zhuǎn)而直接觸發(fā) Full GC凉敲。
第四條說(shuō)到晉升,什么是晉升呢寺旺?YGC 后爷抓,幸存的對(duì)象會(huì)放入到 Survivor 區(qū),如果一個(gè)對(duì)象在多次 YGC 后仍然存活阻塑,則進(jìn)入老年代蓝撇,這個(gè)過(guò)程叫做晉升。每次 YGC 后叮姑,這個(gè)對(duì)象的年齡加一唉地。當(dāng)然,晉升的條件比較復(fù)雜传透。我們后面會(huì)詳細(xì)講述。
4. Mixed GC
G1 專屬GC极颓,這里不準(zhǔn)備講述這個(gè) GC朱盐。
總結(jié)
到這里,我們解釋了3種垃圾回收算法菠隆,第四個(gè)不算是算法兵琳,而是一種設(shè)計(jì)。還大致講了5種收集器骇径,并將這個(gè)坑留在了后面的文章里躯肌,最后講了一些 GC 術(shù)語(yǔ),YGC 破衔,Old GC 清女,F(xiàn)ull GC 等。
好了晰筛,關(guān)于5種垃圾收集器的詳細(xì)介紹嫡丙,我們將在 深入淺出 JVM GC(3)中詳細(xì)說(shuō)明。
good luck6恋凇J锊!