Java垃圾收集算法

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

  相關閱讀:java

  一、深刻理解java虛擬機之java內存區域算法

  二、深刻理解java虛擬機之對象真的死了嗎jvm

 

  一、標記-清除算法

  標記清除算法分爲「標記」和「清除」兩個階段,首先先標記出那些對象須要被回收,在標記完成後會對這些被標記了的對象進行回收;以下圖:post

  這種算法的優勢在於不須要對對象進行移動操做,僅對不存活的對象進行操做,因此在對象存活率較高的狀況下效率很是高,可是從上圖模擬的結果來看對象被回收後,可用的內存並非連續的,而是斷斷續續,形成大量的內存碎片。 存儲對象時要求內存空間時連續的,因此虛擬機在給新的內存較大的對象分配空間時,有可能找不到足夠大的連續的空閒的空間來存放,從而引起一次垃圾回收動做,實際上裏面是有大量的空閒空間的,只是不連續而已。url

  二、複製算法

  複製算法是將內存分爲兩塊大小同樣的區域,每次是使用其中的一塊。當這塊內存塊用完了,就將這塊內存中還存活的對象複製到另外一塊內存中,而後清空這塊內存。這種算法在對象存活率較低的場景下效率很高,好比說新生代,只對整塊內存區域的一半進行垃圾回收,在垃圾回收的過程也不會出現內存碎片的狀況,不須要移動對象,只須要移動指針便可,實現簡單,因此運行效率很高。運行效率是在創建在浪費空間的基礎上的,這是典型的已空間換時間的方法,由於每次只能是使用北村的一半。算法示意圖以下:spa

  

  如今商用的jvm中都採用了這種算法來回收新生代,由於新生代的對象基本上都是朝生夕死的,存活下來的對象約佔10%左右,因此須要複製的對象比較少,採用這種算法效率比較高。hotspot版本的虛擬機將堆(heap)內存分爲了新生代和老年代,其中新生代又分爲內存較大的Eden區和兩個較小的survivor區。當進行內存回收時,將eden區和survivor區的還存活的對象一次性地複製到另外一個survivor空間上,最後將eden區和剛纔使用過的survivor空間清理掉。hotspot虛擬機默認eden和survivor空間的大小比例爲8:1,也就是每次新生代中可用內存空間爲整個新生代空間的90%(80%+10%),只會浪費掉10%的空間。固然,98%的對象可回收只是通常場景下的數據,咱們沒有辦法保證每次回收都只有很少於10%的對象存活,當survivor空間不夠用時,須要依賴於其餘內存(這裏指的是老年代)進行分配的擔保。3d

  三、標記-整理算法

  複製算法在對象存活率較高的狀況下就要進行較多的對象複製操做,效率將會變低。更關鍵的是,若是你不須要浪費50%的空間,就須要有額外的空間進行分配擔保,用以應對被使用的內存中全部對象都100%存活的極端狀況,因此在老年代通常不能直接選用這種辦法。指針

  根據老年代的特色,有人提出了標記-整理的算法,標記過程仍然與標記-清楚算法同樣,但後續步驟不是直接將可回收對象清理掉,而是讓全部存活的對象都向一端移動,而後直接清理掉端邊界之外的內存,算法示意圖以下:視頻

  四、分代收集算法

  分代收集算法將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 觸發條件:

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

  二、老年代空間不足;

  三、方法區空間不足;

  四、重新生代進入老年代的空間大於老年代空閒空間;

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

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

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

  參考資料:《深刻理解Java虛擬機》

 

  喜歡我寫的博客的同窗能夠關注訂閱號【Java解憂雜貨鋪】,裏面不按期發佈一些技術幹活,也能夠免費獲取大量最新最流行的技術教學視頻。

  

 

相關文章
相關標籤/搜索