Java8 的 G1 垃圾回收器相對(duì)于之前的 CMS 有什么特別的呢?
本文轉(zhuǎn)載自微信公眾號(hào)「Java極客技術(shù)」,作者鴨血粉絲。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java極客技術(shù)公眾號(hào)。
CMS
CMS 垃圾回收器,全稱 Concurrent Mark Sweep 并發(fā)標(biāo)記-清除,從名字上面我們也可以看出這個(gè)垃圾回收器是基于標(biāo)記清除算法實(shí)現(xiàn)的。首先"并發(fā)"表示 GC 線程可以和用戶線程并發(fā)執(zhí)行,同時(shí)既然是標(biāo)記-清除算法,說明這個(gè)垃圾回收器會(huì)產(chǎn)生很多碎片,這是標(biāo)記-清除算法的缺點(diǎn)。同時(shí) CMS 是作用于老年代的,老年代的垃圾回收頻率相對(duì)年輕代會(huì)低一點(diǎn)。
CMS 的垃圾回收有四個(gè)過程
- 初始標(biāo)記:
- 并發(fā)標(biāo)記:
- 重新標(biāo)記:
- 并發(fā)清除:
初始標(biāo)記的時(shí)候是一個(gè) STW (stop the world)的過程,所有的用戶線程都會(huì)停止,這個(gè)時(shí)候只是標(biāo)記一下 GC Roots 能直接達(dá)到的對(duì)象,由于只是標(biāo)記一層所以整個(gè)速度相對(duì)會(huì)比較快。
并發(fā)標(biāo)記是一個(gè) GC Roots 掃描的過程,會(huì)掃描整個(gè)鏈路標(biāo)記可以回收的對(duì)象;由于整個(gè)的鏈路會(huì)比較長(zhǎng),所以相對(duì)會(huì)耗時(shí)久一點(diǎn),不過由于這個(gè)過程是并發(fā)的,所以對(duì)用戶線程運(yùn)行是沒有影響的。
重新標(biāo)記顧名思義是一個(gè)再次標(biāo)記的過程,同時(shí)也是會(huì) STW,之所以會(huì)有這個(gè)重新標(biāo)記的過程,是因?yàn)樵谏弦徊讲l(fā)標(biāo)記的過程中,用戶線程依舊在運(yùn)行,所以對(duì)象的引用關(guān)系會(huì)發(fā)生變化同時(shí)在運(yùn)行的時(shí)候也會(huì)產(chǎn)生新的垃圾。這里只會(huì)標(biāo)記在上一步有發(fā)生變化的對(duì)象,雖然會(huì) STW 不過速度也較快。
并發(fā)清除是最后一個(gè)階段,這個(gè)階段由于需要清除之前掃描的所有垃圾對(duì)象,所以會(huì)相對(duì)比較耗時(shí),不過這個(gè)階段是可以并發(fā)進(jìn)行的所以對(duì)用戶線程的運(yùn)行不會(huì)有影響。
經(jīng)過上面的四個(gè)過程就完成了一次完整的 GC,前面我們提到整個(gè) CMS 垃圾回收器是基于標(biāo)記-清除算法的,先通過三個(gè)過程標(biāo)記出需要清理的對(duì)象,然后再進(jìn)行清理。整個(gè)過程中初始標(biāo)記和重新標(biāo)記會(huì)觸發(fā) STW,其他兩個(gè)階段是并發(fā)進(jìn)行的。標(biāo)記-清除算法會(huì)產(chǎn)生內(nèi)存碎片,所以不適合需要頻繁回收的年輕代,所以只適合老年代。產(chǎn)生碎片是 CMS 的缺點(diǎn),并發(fā)是 CMS 的優(yōu)點(diǎn),畢竟任何一個(gè)收集器都會(huì)有優(yōu)缺點(diǎn)。
G1
前面我們聊完了 CMS,接下來我們聊一下 G1,G1 全稱 Garbage First,在講 G1 垃圾回收器的細(xì)節(jié)之前,我們首先要知道的是 G1 對(duì)整個(gè)堆的空間做了重新的定義。G1 中的老年代和年輕代已經(jīng)不再是物理隔離的了,而是邏輯隔離。在 G1 中整個(gè)堆空間被分成了一個(gè)個(gè)相同大小的 Region 塊,多個(gè) Region 塊在邏輯上組成了年輕代和老年代。
這樣做的目的是因?yàn)樵谶M(jìn)行垃圾回收的時(shí)候不需要進(jìn)行整個(gè)堆空間的掃描,同時(shí)可以根據(jù)指定停頓時(shí)間來進(jìn)行垃圾回收。G1 會(huì)將每個(gè) Region 的回收成本進(jìn)行量化,從而達(dá)到一個(gè)成本控制,可以在限定的停頓時(shí)間內(nèi)完成回收,這是 G1 的最大的特點(diǎn)。
G1 回收也分為四個(gè)過程:
- 初始標(biāo)記:初始標(biāo)記與 CMS 也是只掃描 GC Roots 直達(dá)的對(duì)象,這階段同樣也要 STW,不過時(shí)間也很短;
- 并發(fā)標(biāo)記:從 GC Roots 開始堆中對(duì)象進(jìn)行可達(dá)性分析,找出存活的對(duì)象,這個(gè)階段耗時(shí)較長(zhǎng),但是可以與用戶程序并發(fā)執(zhí)行;
- 最終標(biāo)記:最終標(biāo)記和 CMS 的重新標(biāo)記的思路一直,也是為了修正并發(fā)標(biāo)記期間由于用戶程序并發(fā)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象,不過不同的是 G1 會(huì)將這段時(shí)間對(duì)象變化記錄在線程 Remembered Set Logs 里面,最終標(biāo)記階段需要把 Remembered Set Logs 的數(shù)據(jù)合并到 Remembered Set 中,這個(gè)階段需要停頓線程,不過是可并行執(zhí)行;
- 篩選回收:最后一步篩選回收是 G1 與 CMS 最大的不同之處,G1 首先會(huì)對(duì)各個(gè)需要回收的 Region 代價(jià)進(jìn)行量化和排序,在結(jié)合用戶所期望的 GC 停頓時(shí)間來制定回收計(jì)劃,通過-XX:MaxGCPauseMillis 參數(shù)來指定期望的回收時(shí)間。這個(gè)階段也可以做到與用戶程序一起并發(fā)執(zhí)行,但是因?yàn)橹换厥找徊糠? Region,時(shí)間是用戶可控制的,而且停頓用戶線程將大幅提高收集效率。
上面提到了一個(gè) Remembered Set 記憶集,是用來記錄對(duì)象引用的,在并發(fā)標(biāo)記的時(shí)候有對(duì)象引用發(fā)生變更的時(shí)候會(huì)記錄到這里,等到最終標(biāo)記的時(shí)候進(jìn)行修正。整體上來看 G1 采用的是標(biāo)記-整理的算法來進(jìn)行垃圾回收,也不會(huì)像 CMS 那樣會(huì)產(chǎn)生內(nèi)容碎片,所以 G1 同時(shí)可以進(jìn)行年輕代和老年代的垃圾回收,相比 CMS 會(huì)更靈活一點(diǎn),而且也因?yàn)?G1 將內(nèi)存劃分成 Region 了,也不會(huì)造成復(fù)制算法帶來的空間浪費(fèi)的問題。
總結(jié)
首先CMS 和 G1 都是并發(fā)和分代的垃圾回收器,并且都是低延遲的;CMS 是基于標(biāo)記-清除算法的,只適合在年輕代使用,不可預(yù)測(cè)停頓時(shí)間,同時(shí)年輕代和老年代是物理隔離的。G1 是基于標(biāo)記-整理的高吞吐,可預(yù)測(cè)停頓時(shí)間的垃圾回收器,可以同時(shí)使用在年輕代和老年代,同時(shí)年輕代和老年代是邏輯隔離的。
特點(diǎn) | G1 | CMS |
---|---|---|
算法 | 標(biāo)記-整理 | 標(biāo)記-清除 |
年輕代和老年代隔離方式 | 邏輯隔離 | 物理隔離 |
停頓時(shí)間可預(yù)測(cè)行 | 是 | 否 |
并發(fā)和分代 | 支持 | 支持 |
吞吐量 | 高 | 低 |
使用場(chǎng)景 | 年輕代,老年代 | 年輕代 |
低延時(shí) | 是 | 是 |