第四章:JVM垃圾收集算法

①標記/清除算法[最基礎]

後續的GC收集算法都是基於這種思路並對其不足進行改進而獲得的。web

標記/清除算法的基本思想就跟它的名字同樣,分爲「標記」和「清除」兩個階段:首先標記出全部須要回收的對象,在標記完成後統一回收全部被標記的對象。算法

標記階段:標記的過程其實就是前面介紹的可達性分析算法的過程,遍歷全部的GC Roots對象,對從GC Roots對象可達的對象都打上一個標識,通常是在對象的header中,將其記錄爲可達對象;jvm

清除階段:清除的過程是對堆內存進行遍歷,若是發現某個對象沒有被標記爲可達對象(經過讀取對象header信息),則將其回收。spa

 

缺點code

    一、效率問題。標記和清除兩個階段的效率都不高,由於這兩個階段都須要遍歷內存中的對象,不少時候內存中的對象實例數量是很是龐大的,這無疑很耗費時間,並且GC時須要中止應用程序,這會致使很是差的用戶體驗。orm

    二、空間問題。標記清除以後會產生大量不連續的內存碎片(從上圖能夠看出),內存空間碎片太多可能會致使之後在程序運行過程當中須要分配較大對象時,沒法找到足夠的連續內存而不得不提早觸發另外一次垃圾回收動做。對象

 

 

②複製算法
複製算法的原理是:將可用內存按容量劃分爲大小相等的兩塊,每次使用其中的一塊。當這一塊的內存用完了,就將還存活的對象複製到另外一塊內存上,而後把這一塊內存全部的對象一次性清理掉。
 

    複製算法每次都是對整個半區進行內存回收,這樣就減小了標記對象遍歷的時間,在清除使用區域對象時,不用進行遍歷,直接清空整個區域內存,並且在將存活對象複製到保留區域時也是按地址順序存儲的,這樣就解決了內存碎片的問題,在分配對象內存時不用考慮內存碎片等複雜問題,只須要按順序分配內存便可。blog

缺點ip

一、將內存縮小爲原來的一半,浪費了一半的內存空間,代價過高;內存

二、若是對象的存活率很高,極端一點的狀況假設對象存活率爲100%,那麼咱們須要將全部存活的對象複製一遍,耗費的時間代價也是不可忽視的。

應用

    新生成的對象優先存放在新生代中,新生代對象朝生夕死,存活率很低,在新生代中,常規應用進行一次垃圾收集通常能夠回收70% ~ 95% 的空間,回收效率很高

     HotSpot將新生代劃分爲三塊,一塊較大的Eden空間和兩塊較小的Survivor空間,默認比例爲8:1:1。劃分的目的是由於HotSpot採用複製算法來回收新生代,設置這個比例是爲了充分利用內存空間,減小浪費。新生成的對象在Eden區分配(大對象除外,大對象直接進入老年代),當Eden區沒有足夠的空間進行分配時,虛擬機將發起一次Minor GC。

     GC開始時,對象只會存在於Eden區和From Survivor區,To Survivor區是空的(做爲保留區域)。GC進行時,Eden區中全部存活的對象都會被複制到To Survivor區,而在From Survivor區中,仍存活的對象會根據它們的年齡值決定去向,年齡值達到年齡閥值(默認爲15,新生代中的對象每熬過一輪垃圾回收,年齡值就加1,GC分代年齡存儲在對象的header中)的對象會被移到老年代中,沒有達到閥值的對象會被複制到To Survivor區。接着清空Eden區和From Survivor區,新生代中存活的對象都在To Survivor區。接着, From Survivor區和To Survivor區會交換它們的角色,也就是新的To Survivor區就是上次GC清空的From Survivor區,新的From Survivor區就是上次GC的To Survivor區,總之,無論怎樣都會保證To Survivor區在一輪GC後是空的。GC時當To Survivor區沒有足夠的空間存放上一次新生代收集下來的存活對象時,須要依賴老年代進行分配擔保,將這些對象存放在老年代中。

 

③標記/整理算法
在老年代中因爲對象的存活率很是高,內存中對象100%存活的極端狀況複製算法就不合適了。
 
根據老年代的特色,高人們提出了另外一種算法:標記/整理算法。從名字上看,這種算法與標記/清除算法很像,事實上,標記/整理算法的標記過程任然與標記/清除算法同樣,但後續步驟不是直接對可回收對象進行回收,而是讓全部存活的對象都向一端移動,而後直接清理掉端邊線之外的內存。
 

回收前:

       

回收後:

       

 

回收後可回收對象被清理掉了,存活的對象按規則排列存放在內存中。這樣一來,當咱們給新對象分配內存時,jvm只須要持有內存的起始地址便可。

標記/整理算法不只彌補了標記/清除算法存在內存碎片的問題,也消除了複製算法內存減半的高額代價,可謂一箭雙鵰。

缺點,標記/整理算法的缺點就是效率也不高,不只要標記存活對象,還要整理全部存活對象的引用地址,在效率上不如複製算法。

 

 

④分代收集算法[ 終極算法 ]

    當前商業虛擬機都採用分代收集算法。

    分代收集算法的思想是按對象的存活週期不一樣將內存劃分爲幾塊,通常是把Java堆分爲新生代和老年代(還有一個永久代,是HotSpot特有的實現,其餘的虛擬機實現沒有這一律念,永久代的收集效果不好,通常不多對永久代進行垃圾回收),這樣就能夠根據各個年代的特色採用最合適的收集算法。

    新生代:朝生夕滅,存活時間很短。

    老年代:通過屢次Minor GC而存活下來,存活週期長。

    在新生代中每次垃圾回收都發現有大量的對象死去,只有少許存活,所以採用複製算法回收新生代,只須要付出少許對象的複製成本就能夠完成收集;而老年代中對象的存活率高,不適合採用複製算法,並且若是老年代採用複製算法,它是沒有額外的空間進行分配擔保的,所以必須使用標記/清理算法或者標記/整理算法來進行回收。

    總結一下就是,分代收集算法的原理是採用複製算法來收集新生代,採用標記/清理算法或者標記/整理算法收集老年代。

 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相關文章
相關標籤/搜索