天天看點

JVM GC和常見垃圾回收算法

1 GC要做的四件事

JVM GC和常見垃圾回收算法

2 判斷哪些是垃圾

JVM GC和常見垃圾回收算法

2.1 引用計數法

在Java中,引用和對象是有關聯的,如果要操作對象必須用引用進行,是以很顯然一個辦法就是​

​通過引用計數來判斷該對象是否可以回收​

​,簡單講,如果一個對象沒有任何與之關聯的引用,即他的引用計數都不為0,則說明對象不太可能被用到,那麼這個對象就是可回收對象。

2.2 根搜尋算法(可達性分析)

因為引用計數法存在引用循環的問題,Java還使用了可達性分析的方法,通過一系列的“GC roots”對象作為起點搜尋,如果在“GC roots”和一個對象之間沒有可達的路徑,則稱該對象是不可達的。注意的是不可達對象不一定是可回收對象,​

​需要經過兩次不可達标記後才能被确定為可回收對象​

​。

JVM GC和常見垃圾回收算法

3 怎麼回收垃圾(垃圾回收算法)

JVM GC和常見垃圾回收算法

3.1 标記清除法(Mark-Sweep)

最簡單的垃圾回收算法,分為兩個階段,​

​标記和清除​

​,标記階段标記出所有将要回收的對象,清除階段将被标記的對象空間進行回收。

JVM GC和常見垃圾回收算法

(該圖檔來着網絡,如有侵權請聯系删除)

遺留問題:記憶體的碎片化嚴重,後續可能會産生浪費。

3.2 複制算法(Coping)

為了解決标記清除法的記憶體碎片化問題,提出了複制算法,主要過程是将記憶體按容量相等的條件劃分成兩塊,每次隻使用其中的一般,當這一塊記憶體滿後将存活的對象複制到另一塊上去,把已使用的清除掉。

JVM GC和常見垃圾回收算法

(該圖檔來着網絡,如有侵權請聯系删除)

遺留問題:可用記憶體被壓縮到了原來的一半,而且如果存活對象增多的話該算法的效率會降低

3.3 标記整理算法(Mark-Compact)

和Mark-Sweep算法相同,标記後不是清理對象,而是将存活對象移動到記憶體的另一端,然後删除端邊界外的對象。

JVM GC和常見垃圾回收算法

(該圖檔來着網絡,如有侵權請聯系删除)

3.4 分代收集算法(Generational Collecting)

目前大部分JVM采用的垃圾回收算法,其核心思想是根據對象存活的不同生命周期将記憶體劃分為不同的區域,一般情況下将GC堆劃分為老年代、新生代,相比之下新生代的回收次數和單次回收的數量要大于老年代。

3.4.1 新生代與複制算法
JVM GC和常見垃圾回收算法

​每次使用Eden空間和其中的一塊Survivor空間​

​,當進行回收時,将該兩塊空間中還存活的對象複制到另一塊Survivor空間中。

3.4.2 老年代與标記複制算法

因為老年代每次回收對象較少,是以采用Mark-Compact算法。

當Eden Space和From Space記憶體不足時會發生一次GC,結束後存活的對象将會轉移到To Space,當To Space記憶體不夠時就會移動到老年代,對象在Survivor區躲過一次GC後,其年齡會+1 ,預設下年齡到15時對象會被移動到老年代。

4 不同記憶體區域垃圾的回收方式

4.1 新生代

使用Minor GC進行回收,采用複制算法,年輕代分為Eden區和Survivor 區。

Eden區:對象剛被建立的時候,存放在Eden 區,如果Eden區放不下,則放在Survivor區,甚至老年代中。

Survivor區: Minor回收時使用,将Eden中存活的對象存入Survior中(From),再一次Minor時,将Survior From 中的對

象存入Survior To中,清除Survior From,下一次Minor時重複次步驟,Survior From變成Survior To,Survior To變成

Survior From,依次循環,同時每次 Minor,對象的年齡都+1,年齡增加到一定程度的對象,移動到老年代中。

4.2 老年代

存放生命周期較長的對象,使用标記-清理算法或者标記-整理算法進行回收。

5 擴充:常見的垃圾收集器