判斷對象是否存活
-
引用計(jì)數(shù)算法
定義: 給對象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有地方對其進(jìn)行引用,計(jì)數(shù)器數(shù)值加1,當(dāng)引用失效時(shí),計(jì)數(shù)器就減1,任何時(shí)刻計(jì)數(shù)器為0的對象就是不可能再被使用的。
但是Java虛擬機(jī)中并沒有使用計(jì)數(shù)算法來管理內(nèi)存,因?yàn)樗茈y解決對象之間相互循環(huán)引用的問題。如下代碼中這種相互引用,導(dǎo)致引用計(jì)數(shù)一直不為0
public class RefrenceGC(){
public Object instance = null;
public static void testGC(){
RefrenceGC objA = new RefrenceGC();
RefrenceGC objB = new RefrenceGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
}
}
-
可達(dá)性分析算法
在主流的商用程序語言(Java,C#,Lisp)的主流實(shí)現(xiàn)中,都是稱通過可達(dá)性分析來判定對象是否存活。
思路: 通過一系列稱為“GC Roots”的對象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索走過的路徑稱為引用鏈(Reference Chain),當(dāng)一個(gè)對象到GC Roots沒有任何引用鏈相連時(shí)(用圖論的話來說,就是從GC Roots到這個(gè)對象不可達(dá)),則證明此對象是不可用的。
圖中對象object5,object6,object7雖然相互有關(guān)聯(lián),但是它們到GC Roots是不可達(dá)的,因?yàn)樗鼈儗慌卸榭苫厥諏ο蟆?/p>
生存還是死亡
即使在可達(dá)性分析后的不可達(dá)對象,也不是“非死不可”,如果要真正宣告死亡,至少經(jīng)歷兩次標(biāo)記過程:如果對象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈,就會被第一次進(jìn)行標(biāo)記并且進(jìn)行一次刪選,判斷是否有必要執(zhí)行finalize()方法,當(dāng)對象沒有覆蓋finalize()方法,或者finalize()方法已經(jīng)被虛擬機(jī)調(diào)用過,即會被判為沒有必要執(zhí)行。
如果被判定為有必要執(zhí)行的finalize()方法,對象將會被放置在F-Queue的隊(duì)列中,會有個(gè)低優(yōu)先級的Finalizer線程去執(zhí)行,finalize()方法是對象逃脫死亡命運(yùn)的最后一次機(jī)會,如果要拯救自己,就需要重新與引用鏈上的任意一個(gè)對象建立起關(guān)聯(lián)即可。
垃圾收集算法
標(biāo)記-清除算法
對需要回收的進(jìn)行標(biāo)記,然后清除標(biāo)記對象。
缺點(diǎn):
1、效率低,標(biāo)記和清除兩個(gè)過程的效率都不高
2、空間問題,標(biāo)記清除后產(chǎn)生大量不連續(xù)的內(nèi)存碎片,在下次分配較大的對象時(shí),無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾回收。
復(fù)制算法
將內(nèi)存按照等容量分為兩塊,每次只使用其中一塊,一塊用完,將還存活的復(fù)制到另一塊上,然后進(jìn)行整塊清除。
缺點(diǎn):內(nèi)存縮為原來的一半
商業(yè)化虛擬機(jī)采用此種方式,不按標(biāo)準(zhǔn)的1:1劃分,而是劃分一塊大的Eden 和兩塊小的Survivor空間,每次使用Eden和一塊小的Survivor,使用完,將存活的復(fù)制到另一塊小的Survivor上,然后清除Eden和那塊Survivor。默認(rèn)Eden 和Survivor比為8:1。
標(biāo)記-整理算法
標(biāo)記后,讓所有存活的往一端移動,直接清理端邊界外面的內(nèi)存
分代收集算法
一般劃分為新生代和老年代,新生代少量對象存活,采用復(fù)制算法,老年代對象存活率高,采用標(biāo)記-清除或者標(biāo)記-整理方法去處理。