概述
在java對象被GC宣告死亡之前至少要經(jīng)歷兩次標(biāo)記過程谜慌,如果對象沒有與GC Roots相連的引用鏈(這里使用的算法是可達性分析算法)摔竿,第二次就是是否調(diào)用對象的finalize(),如果調(diào)用了讽营,并且在finalize()方法里面對象重新獲取了GC Roots的引用说铃,那么對象依舊可以存活下去佑惠,否則將會被清除。
1.第一次標(biāo)記
當(dāng)JVM觸發(fā)一次 minor GC(這里具體指的是針對java堆新生代的垃圾回收萍虽,因為在有的虛擬機里有可能對方法區(qū)進行垃圾回收)時睛廊,如果對象在此時沒有與GC Roots的引用鏈,那么對象就會第一次被標(biāo)記杉编,可作為GC Roots大概如下幾種:
1.虛擬機棧引用的對象超全。
2.方法區(qū)中靜態(tài)屬性引用的對象咆霜。
3.方法區(qū)中常量引用的對象。
4.本地方法棧中Native方法引用的對象卵迂。
5.JVM內(nèi)部引用裕便,如基本數(shù)據(jù)類型對應(yīng)的Class對象,一些常駐的異常對象见咒,還有系統(tǒng)類加載器偿衰。
6.所以被同步鎖(synchronized關(guān)鍵字)持有的對象。
7.反映Java虛擬機內(nèi)部情況的JMXBean改览、JVMTI中注冊的回調(diào)下翎、本地代碼緩存等。
2.第二次標(biāo)記
在第二次標(biāo)記的時候宝当,對象只有一次機會可以逃脫被清除的命運视事,就是程序員重寫了finalized()方法、并且是這個對象第一次觸發(fā)GC庆揩,并且finalized()方法內(nèi)部為這個對象重新獲取了GC Roots的引用俐东。所以如果想要一個被GC標(biāo)記了的對象不被清除,可以重寫當(dāng)前對象的finalized()方法(注意:只有第一次觸發(fā)GC才會調(diào)用此方法订晌,第二次不會調(diào)用)
演示代碼如下(代碼為《深入理解java虛擬機》代碼清單3-2)
/**
* 此代碼演示了兩點:
* 1.對象可以在被GC時自我拯救虏辫。
* 2.這種自救的機會只有一次,因為一個對象的finalize()方法最多只會被系統(tǒng)自動調(diào)用一次
*
* @author zzm
*/
public class FinalizeEscapeGC {
public static FinalizeEscapeGC SAVE_HOOK = null;
public void isAlive() {
System.out.println("yes, i am still alive :)");
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize method executed!");
FinalizeEscapeGC.SAVE_HOOK = this;
}
public static void main(String[] args) throws Throwable {
SAVE_HOOK = new FinalizeEscapeGC();
//對象第一次成功拯救自己
SAVE_HOOK = null;
System.gc();
// 因為Finalizer方法優(yōu)先級很低锈拨,暫停0.5秒砌庄,以等待它
Thread.sleep(500);
if (SAVE_HOOK != null) {
SAVE_HOOK.isAlive();
} else {
System.out.println("no, i am dead :(");
}
// 下面這段代碼與上面的完全相同,但是這次自救卻失敗了
SAVE_HOOK = null;
System.gc();
// 因為Finalizer方法優(yōu)先級很低奕枢,暫停0.5秒娄昆,以等待它
Thread.sleep(500);
if (SAVE_HOOK != null) {
SAVE_HOOK.isAlive();
} else {
System.out.println("no, i am dead :(");
}
}
}
結(jié)果:
finalize method executed!
yes, i am still alive :)
no, i am dead :(
本文參考了周志明大大的《深入理解java虛擬機》第三版,特此說明