如何判斷對(duì)象死亡及垃圾收集算法和垃圾收集器

垃圾收集器在對(duì)堆進(jìn)行回收前,第一件事情就是要確定對(duì)象中哪些還“活著”,哪些“已死”。

一、如何判斷對(duì)象已死

1) 引用計(jì)數(shù)算法

給對(duì)象添加一個(gè)引用計(jì)數(shù)器,每當(dāng)又一個(gè)地方應(yīng)用它時(shí),計(jì)數(shù)器值加1;當(dāng)引用失效時(shí),計(jì)數(shù)器值減1;任何時(shí)刻計(jì)數(shù)器值為0的對(duì)象是不可能再被使用的。
引用計(jì)數(shù)器算法實(shí)現(xiàn)簡(jiǎn)單,判定效率也很高,在大部分情況下是個(gè)不錯(cuò)的算法,但是在主流的Java虛擬機(jī)里沒有選用引用計(jì)數(shù)算法來(lái)管理內(nèi)存,其主要原因它很難解決對(duì)象之間的相互循環(huán)引用的問(wèn)題

2) 可達(dá)性分析算法

這個(gè)算法就是通過(guò)一系列的稱為“GC Roots”的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過(guò)的路徑稱為引用鏈(Reference Chain),當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連時(shí),則證明此對(duì)象是不可用的。


可達(dá)性分析算法判定對(duì)象是否可回收.png

可作為GC Roots的對(duì)象包括下面幾種:
1.虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象;
2.方法區(qū)中類靜態(tài)屬性引用的對(duì)象;
3.方法區(qū)中常量應(yīng)用的對(duì)象;
4.本地方法棧中JNI(即一般說(shuō)的Native方法)引用的對(duì)象;

JDK1.2后,Java對(duì)應(yīng)用的概念進(jìn)行擴(kuò)充,將引用分為強(qiáng)引用,軟引用,弱引用,虛引用
強(qiáng)引用類似“Object obj = new Object()”這類引用,只要強(qiáng)引用還存在,垃圾回收器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象。
軟引用有用非必須的對(duì)象,當(dāng)發(fā)生內(nèi)存溢出時(shí)會(huì)把這些對(duì)象列進(jìn)回收范圍之中進(jìn)行第二次回收。提供了SoftReference類來(lái)實(shí)現(xiàn)軟引用。
弱引用也是非必須對(duì)象的,只能生存都下次垃圾收集發(fā)生之前。提供了WeakReference類來(lái)實(shí)現(xiàn)引用。
虛引用稱為幽靈引用或者幻影引用,一個(gè)對(duì)象是否是有虛引用存在,完全不對(duì)堆其生存時(shí)間構(gòu)成影響,也無(wú)法通過(guò)一個(gè)虛引用來(lái)獲取一個(gè)對(duì)象的實(shí)例。為一個(gè)對(duì)象設(shè)置虛引用的唯一目的就是能在這個(gè)對(duì)象被垃圾收集器回收是收到一個(gè)系統(tǒng)通知。提供PhantomReference類來(lái)實(shí)現(xiàn)虛引用。

二、垃圾收集算法

1) 標(biāo)記-清除算法

首先是標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收所有標(biāo)記的對(duì)象。
主要的不足:
一個(gè)是效率問(wèn)題,標(biāo)記和清除兩個(gè)過(guò)程效率都不高;
另一個(gè)是空間問(wèn)題,標(biāo)記清除之后會(huì)產(chǎn)生大量的不連續(xù)的內(nèi)存碎片,空間碎片太多可能會(huì)導(dǎo)致以后在程序運(yùn)行過(guò)程中需要分配較大對(duì)象時(shí),無(wú)法找到足夠的連續(xù)內(nèi)存而不得不出發(fā)另一次垃圾收集動(dòng)作

2) 復(fù)制算法

他將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活的對(duì)象復(fù)制到另一塊上,然后把已使用過(guò)的內(nèi)存空間一次清理掉。
這種算法實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行效率高。不足是將內(nèi)存縮小為yuanli的一半,代價(jià)未免太高。
商業(yè)虛擬機(jī)都采用這種收集算法來(lái)回收新生代。新生代的對(duì)象98%是“朝生夕死”,所以并不需要按照1:1的比例來(lái)劃分內(nèi)存空間,而是將內(nèi)存分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor。當(dāng)回收是,將Eden和Survivor中還存活的對(duì)象一次性復(fù)制到另一個(gè)Survivor空間上,最后清理掉Eden和剛才用過(guò)的Survivor空間。HotSpot虛擬機(jī)默認(rèn)Eden和Survivor的大小比例是8:1,由于我們沒法保證每次護(hù)手都不多余10%的對(duì)象存活,當(dāng)Survivor空間不足時(shí),需要依賴其它內(nèi)存(這里指老年代)進(jìn)行分配擔(dān)保。

3) 標(biāo)記-整理算法

復(fù)制收集算法在對(duì)象存活率較高時(shí)就要進(jìn)行較多的復(fù)制操作,效率將會(huì)變低。更關(guān)鍵的是如果不想浪費(fèi)50%的空間,就需要額外的空間擔(dān)保,以應(yīng)對(duì)被使用的內(nèi)存中所有對(duì)象都100%存活的極端情況,所以老年代一般不能直接選用這種算法。
標(biāo)記-整理算法,首先是標(biāo)記出所有存活的對(duì)象,將存活的對(duì)象都移向一端,然后清理掉端邊界以外的內(nèi)存。

3) 分代收集算法

根據(jù)對(duì)象存活的周期不同將內(nèi)存劃分為幾塊。一般把Java堆分為新生代和老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴?。在新生代中,每次垃圾收集時(shí)都發(fā)現(xiàn)有大批的對(duì)象死去,只有少量存活,那就選用復(fù)制算法,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集。而老年代存活率高、沒有額外空間對(duì)它分配擔(dān)保,就必須使用“標(biāo)記-清理”或者“標(biāo)記-整理”算法來(lái)進(jìn)行回收。

三、垃圾收集器

基于JDK1.7 Update 14之后的HotSpot虛擬機(jī),這個(gè)虛擬機(jī)包含的所有收集器如圖:


HotSpot虛擬機(jī)的垃圾收集器.png

現(xiàn)在用的基本都是CMS,則這里只說(shuō)CMS收集器:CMS收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。目前很大一部分的Java應(yīng)用集中在互聯(lián)網(wǎng)網(wǎng)站或者B/S系統(tǒng)的服務(wù)端上,這類應(yīng)用尤其重視服務(wù)的響應(yīng)速度,希望系統(tǒng)停頓時(shí)間最短,以給用戶帶來(lái)較好的體驗(yàn)。

CMS優(yōu)點(diǎn)就是:并發(fā)收集,低停頓
CMS缺點(diǎn):
1)CMS對(duì)CPU資源非常敏感,會(huì)導(dǎo)致應(yīng)用程序慢,總吞吐量降低;
2)CMS無(wú)法處理浮動(dòng)垃圾;
3)是一款基于"標(biāo)記-清除"算法實(shí)現(xiàn)的會(huì)產(chǎn)生大量的空間碎片。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容