Java之垃圾收集GC詳解(2)

垃圾收集算法

由於垃圾收集算法的實現涉及大量的程序細節,而且每個平臺的虛擬機操作內存的方法又各不相同,因此博客中不過多的討論算法的實現,只是介紹幾種算法的思想以及發展。

1、分代收集算法

分代收集算法將heap區域劃分爲新生代和老年代,新生代的空間比老年代的空間要小。新生代又分爲了Eden和兩個survivor空間,它們的比例爲8:1:1。對象被創建時,內存的分配是在新生代的Eden區發生的,大對象直接在老年代分配內存,IBM的研究表明,Eden區98%的對象都是很快消亡的。

  爲了提高gc效率,分代收集算法中新生代和老年代的gc是分開的,新生代發生的gc動作叫做minor gc 或 young gc,老年代發生的叫做major gc 或 full gc。

  minor gc 的觸發條件:當創建新對象時Eden區剩餘空間小於對象的內存大小時發生minor gc;

  major gc 觸發條件:

  1、顯式調用System.gc()方法;

  2、老年代空間不足;

  3、方法區空間不足;

  4、從新生代進入老年代的空間大於老年代空閒空間;

  Eden區對象的特點是生命週期短,存活率低,因此Eden區使用了複製算法來回收對象,上面也提到複製算法的特點是在存活率較低的情況下效率會高很多,因爲需要複製的對象少。與一般的複製算法不同的是,一般的複製算法每次只能使用一半的空間,另一半則浪費掉了,Eden區的回收算法也叫做"停止-複製"算法,當Eden區空間已滿時,觸發Minor GC,清理掉無用的對象,然後將存活的對象複製到survivor1區(此時survivor0有存活對象,survivor1爲空的),清理完成後survivor0爲空白空間,survivor1有存活對象,然後將survivor0和survivor1空間的角色對象,下次觸發Minor gc時重複上述過程。如果survivor1區剩餘空間小於複製對象所需空間時,將對象分配到老年代中。每發生一次Minor gc時,存活下來的對象的年齡則會加1,達到一定的年齡後(默認爲15)該對象就會進入到老年代中。

  老年代的對象基本是經過多次Minor gc後存活下來的,因此他們都是比較穩定的,存活率高,如果還是用複製算法顯然是行不通的。所以老年代使用“標記-整理”算法來回收對象的,從而提高老年代回收效率。

  總的來說,分代收集算法並不是一種具體的算法,而是根據每個年齡代的特點,多種算法結合使用來提高垃圾回收效率。

2、標記-清除算法

標記清除算法分爲“標記”和“清除”兩個階段,首先先標記出那些對象需要被回收,在標記完成後會對這些被標記了的對象進行回收

如圖:

優點:不需要對對象進行移動操作,僅對不存活的對象進行操作,所以在對象存活率較高的情況下效率非常高。

缺點:執行效率不穩定。從上圖模擬的結果來看對象被回收後,可用的內存並不是連續的,而是斷斷續續,造成大量的內存碎片。 存儲對象時要求內存空間時連續的,所以虛擬機在給新的內存較大的對象分配空間時,有可能找不到足夠大的連續的空閒的空間來存放,從而引發一次垃圾回收動作,實際上裏面是有大量的空閒空間的,只是不連續而已。

3、標記-複製算法

複製算法是將內存分爲兩塊大小一樣的區域,每次是使用其中的一塊。當這塊內存塊用完了,就將這塊內存中還存活的對象複製到另一塊內存中,然後清空這塊內存。這種算法在對象存活率較低的場景下效率很高,比如說新生代,只對整塊內存區域的一半進行垃圾回收,在垃圾回收的過程也不會出現內存碎片的情況,不需要移動對象,只需要移動指針即可,實現簡單,所以運行效率很高。運行效率是在建立在浪費空間的基礎上的,這是典型的空間換時間的方法。

如圖:

4、標記-整理算法

複製算法在對象存活率較高的情況下就要進行較多的對象複製操作,效率將會變低。更關鍵的是,如果你不需要浪費50%的空間,就需要有額外的空間進行分配擔保,用以應對被使用的內存中所有對象都100%存活的極端情況,所以在老年代一般不能直接選用這種辦法。

  根據老年代的特點,有人提出了標記-整理的算法,標記過程仍然與標記-清楚算法一樣,但後續步驟不是直接將可回收對象清理掉,而是讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的內存。

如圖:

 

發佈了33 篇原創文章 · 獲贊 29 · 訪問量 4776
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章