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