現(xiàn)有的垃圾回收算法
分類
根據(jù)如何判定對象是垃圾,垃圾回收算法分為兩類:1、 「引用計數(shù)式垃圾收集」 (判定垃圾是通過引用計數(shù)器)別名:直接垃圾收集 2、 「追蹤式垃圾收集」 (判定垃圾是通過GC Roots)別名:間接垃圾收集
主流虛擬機采用的是第二種追蹤式垃圾收集,所以本文講解第二種垃圾收集的算法
垃圾收集器的設計原則
根據(jù)兩個分代假說:
1.絕大部分對象是熬不過第一次垃圾回收的 2.熬過多次垃圾回收的對象是難以被標記為垃圾的。
垃圾收集器將堆中的內存劃分為了不同的區(qū)域,根據(jù)對象分代年齡(熬過多少次垃圾回收)來分配到不同的區(qū)域中:
比如對象分代年齡小的,第一種對象就應該 「標記存活對象」 即可,而不需要標記那些垃圾對象,因為這部分對象大部分都是很快用完就不用的垃圾對象。
而第二種對象分代年齡大的,則應該標記的是垃圾對象,因為根據(jù)第二個假說這部分對象中垃圾對象的占比很少,所以垃圾回收的頻率也可以降低。
「將堆劃分為不同的區(qū)域后,垃圾回收器可以只回收其中一部分區(qū)域,針對每一部分區(qū)域也可以采用不同的算法來進行回收垃圾?!?/strong>
一般來說堆中至少會被劃分為“新生代”和“老年代”兩個區(qū)域。新生代存儲第一種假說類型的對象,老年代存放第二種假說類型的對象。
「注意」 :這種設計看起來是完美的,但是如果 「老年代中的對象引用了新生代中的對象」 這個時候年輕代發(fā)生垃圾回收時,除了需要遍歷GC Roots外,還需要 「遍歷整個老年代」 才會確保年輕代中的對象真正沒有對象引用。顯然這種遍歷整個老年代效率肯定會很低,所以采用了一種解決方案:讀者有興趣可以看看: 在這篇博客的末尾
標記-清除算法
最早出現(xiàn)的垃圾回收算法,之后出現(xiàn)的算法都是根據(jù)其缺點來進行演進的。
兩個階段:1. 「標記」 2.「清除」「標記需要回收的對象完成后進行統(tǒng)一回收所有被標記的對象, 也可以標記存活的對象統(tǒng)一回收沒有被標記的對象?!?/strong>
「一,標記」 :如何判定對象是否是垃圾的過程在上一篇 博客 中已經(jīng)講解過,接著 「標記」 這些垃圾對象。
「二,清除」 :進行統(tǒng)一回收掉標記的對象。
缺點
1.當堆中的對象大部分是垃圾時, 「標記和清除的效率會變低」 ,而且會隨著內存中垃圾對象的增長,導致效率越來越低。
- 「內存碎片化」 :因為內存分配不是連續(xù)的,所以當清除后,內存中會存在大量內存碎片。當遇到大對象分配內存找不到足夠的連續(xù)的內存來存放時會提前觸發(fā)GC。
標記-復制算法
采用的是“半?yún)^(qū)復制”的算法來實現(xiàn)的,即每次只使用其中的一部分內存,當這部分內存用完后將存活著的對象復制到另外一塊內存上,接著清空剛才使用的那部分內存,當另一部分內存滿了的時候再用上一次清空后的那塊內存往復。
解決了標記-清除的內存碎片化問題,因為當發(fā)生GC時會進行全部清空,只將存活對象復制到另外一塊內存中。
在這里插入圖片描述
“Apple回收策略”
Andrew Appel針對剛剛分代假說中的第一條,提出了“Appel式回收策略”。
一般情況下百分之九十八的對象在經(jīng)歷第一次gc時就會被清除。因此做出優(yōu)化將年輕代分為了 「一塊eden空間和兩塊Survival空間」 。enen和Survival內存占比為 「8:1」 ,即每次使用百分之九十的內存, 「只有百分之十的內存會被浪費」 ,因為對象大部分都會死去所以沒有必要分配一半的空間來存放存活對象。
但是如果使用百分之十的內存來存放存活對象,當存活對象在Survival空間存放不下時,這個時候就需要用老年代擔保,因此當存不下時會存放到老年代中。
缺點
1.當內存中的對象存活率較高時,復制大量存活對象會使得效率變低。2.如果不想造成嚴重的內存浪費,就需要有額外的空間進行分配。
標記-整理算法
上面所說的標記-復制算法針對與新生代中進行的回收算法,并不適用與老年代,原因:老年代中存活率較高,存放不下時需要額外的內存空間擔保。
因此出現(xiàn)了標記-整理算法, 和標記-復制算法相同的是: 「標記的都是存活對象」 ;和標記-復制算法不同的是: 「復制算法將存活對象復制到另外一塊內存上然后清除之前使用的內存,而整理算法是移動存活的對象到一端,然后清除邊界以外的內存」 。
缺點
「移動存活對象」 :對于老年代中GC之后大部分都為存活對象,將這些對象都進行移動并且更新引用這些對象的地方是一個比較耗時的操作。而且更新引用需要暫停用戶線程來保證用戶線程訪問對象不會出錯,簡稱STW,"Stop the Word"。
其實不止整理算法里面移動對象更新引用需要STW,清除算法和復制算法中的標記清除都需要STW,只不過時間短
在這里插入圖片描述
總結
采用標記-整理算法意味著GC的時候要移動對象更新對象的引用,也就是說 「內存回收的時候會更復雜」 。
采用標記-清除算法意味著 「內存碎片化」 。
采用標記-復制算法意味著 「內存可用度不高」 。
“吞吐量”:賦值器(使用垃圾回收器的線程也就是用戶線程)與垃圾回收器的效率總和。
因為 「內存分配和訪問(內存碎片化導致過多的分配和訪問)比垃圾回收器回收的頻率(回收時需要STW,而且比較耗時)要高」 ,因此整理這種方式其實吞吐量要比清除要好。
對于關注吞吐量的收集器Parallel Scabenge基于標記-整理算法, 對于關注STW的收集器CMS來說采用的是標記-清除算法。其實CMS是一種將兩種算法混合起來的收集器,大部分時間采用清除算法,只有當分配內存不足(碎片化特別嚴重)時用整理算法進行一次收集。
-
算法
+關注
關注
23文章
4630瀏覽量
93356 -
計數(shù)器
+關注
關注
32文章
2261瀏覽量
94983 -
GC
+關注
關注
0文章
9瀏覽量
17103 -
JVM
+關注
關注
0文章
158瀏覽量
12260
發(fā)布評論請先 登錄
相關推薦
評論