Java GC過程筆記

本文參考自:馬士兵老師JVM基礎(chǔ)入門丨GC基礎(chǔ)知識(shí)丨GC算法丨JVM的垃圾回收器丨

只看了馬老師關(guān)于GC這部分的講解,后面的調(diào)優(yōu)沒有看,然后做一個(gè)基本的關(guān)于GC過程的筆記。
本文分成如下幾個(gè)部分:
對(duì)象的生命周期
GC的主要的算法概述

對(duì)象的生命周期
馬士兵老師這張圖總結(jié)得太牛逼了,我直接拿過來做了點(diǎn)筆記:

JVM首先會(huì)看一下這個(gè)對(duì)象是不是比較大的對(duì)象,如果不是比較大的對(duì)象可以直接分配在虛擬機(jī)棧中,在方法調(diào)用完成之后會(huì)直接彈出,不需要GC的介入。如果這個(gè)對(duì)象特別大,也不會(huì)直接分配在堆上的Eden區(qū),會(huì)直接直接進(jìn)入老年代。如果對(duì)象的內(nèi)存中等大,考慮首先分配在TLAB,也就是ThreadLocalAllocationBuffer上。設(shè)計(jì)TLAB這一塊是為了避免線程需要同步內(nèi)存分配的問題。
TLAB也在Eden,所以其實(shí)也是分配在了Eden。后續(xù)的過程就會(huì)通過GC。
如果需要GC,首先會(huì)被移動(dòng)到第一個(gè)Survivor,之后如果還是存活的話會(huì)在第一個(gè)和第二個(gè)Survivor之間反復(fù)橫跳,直到達(dá)到了分代年齡,就會(huì)被放到老年代。
GC算法概述
堆的分代年齡模型
堆上的內(nèi)容的劃分是根據(jù)【代】來劃分了,也就是分代年齡。

GC算法主要分成三種:
1. Mark-Sweep
Mark-Sweep算法類似于遞歸的過程,將可達(dá)的目標(biāo)標(biāo)記,回收沒有標(biāo)記的對(duì)象。算法本身比較簡(jiǎn)單,但是會(huì)造成碎片化的問題。
2. Mark-compact
Mark-Compat和MS類似,但是清除垃圾之后也會(huì)整理內(nèi)存碎片。缺點(diǎn)是效率比較低。
3.?copying算法
在MS算法中,如果死亡的對(duì)象比較多,在清除過程中還需要掃描死亡的對(duì)象,這樣就比較耗時(shí)。使用copying算法的話,只需要將存活的對(duì)象從一個(gè)內(nèi)存部分復(fù)制到另外的一個(gè)內(nèi)存部分即可。但是這個(gè)方法本身也需要開辟另外的一部分內(nèi)存區(qū)域用于存放對(duì)象。
在堆中,年輕代使用的是copying算法,在老年代使用的是MS或者是MC算法。
GC中實(shí)際采用的算法是上面的算法的組合:

最原始的算法:
Serial & Serial Old:適用于內(nèi)存比較小的情況。Serial是一個(gè)STW的算法,在GC線程執(zhí)行的過程中,工作線程會(huì)被暫停。并且這類的算法是使用一個(gè)線程串行執(zhí)行的。Serial Old就是使用MS或者是MC算法執(zhí)行GC,主要是針對(duì)老年代。
Parallel Scanvenge& Parallel Old:是一種并行的算法,主要使用copying算法;然后Parallel Old是在多線程上使用MC的GC算法。
在上述的算法中,并不是使用越多線程越高效,而且上述的GC算法都是STW的算法。所以現(xiàn)代的GC使用了名為CMS的算法:
Concurrent Mark Sweep。
這是一種并發(fā)的算法,主要分成四個(gè)部分:
初始標(biāo)記:首先標(biāo)記GC Root,這個(gè)過程是STW的
使用多個(gè)線程并發(fā)的方式標(biāo)記GC Root引用鏈上的其他的節(jié)點(diǎn),這里不需要STW
重新標(biāo)記:上面一步中沒有使用STW,在這段過程中有些對(duì)象可能會(huì)被重新引用到,所以又需要重新標(biāo)記,避免錯(cuò)誤
并發(fā)清理:標(biāo)記完成的對(duì)象清除掉
