JVM 怎么判斷對(duì)象已經(jīng)死了?
在Java中程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧這三個(gè)區(qū)域隨線程而生,隨線程而滅:棧中的棧幀隨著方法的調(diào)用和退出而有條不紊的進(jìn)行著入棧和出棧的過程。
每個(gè)棧幀分配多少內(nèi)存在類結(jié)構(gòu)確定下來時(shí)就已知的,方法結(jié)束或者線程結(jié)束內(nèi)存自然跟著回收了。
而Java堆和方法區(qū)不一樣,一個(gè)接口中的多個(gè)實(shí)現(xiàn)類的內(nèi)存可能不一樣,每個(gè)方法的多個(gè)分支需要的內(nèi)存也可能不一樣,我們只有在程序運(yùn)行時(shí)候才知道會(huì)創(chuàng)建哪些對(duì)象,這部分內(nèi)存的分配和回收都是動(dòng)態(tài)的。
1)引用計(jì)數(shù)算法
給對(duì)象添加一個(gè)引用計(jì)數(shù)器,每當(dāng)一個(gè)地方引用它時(shí)候,計(jì)數(shù)器就加1,當(dāng)引用失效,計(jì)數(shù)器就減1;任何時(shí)刻計(jì)數(shù)器為0的對(duì)象就是不可能再被使用了。
這種方法實(shí)現(xiàn)簡(jiǎn)單,效率高,但是它很難解決對(duì)象的循環(huán)引用問題:
public class Test {private static final int _1MB = 1024 *1024;private Object instance = null;public static void testGC(){Test objectA = new Test();Test objectB = new Test();objectA.instance = objectB;objectB.instance = objectA;objectA = null;objectB = null;// 假如這里發(fā)生GC,objectA和objectB會(huì)不會(huì)被回收}}
2)可達(dá)性分析算法
這個(gè)算法的基本思路是通過一系列稱為“GC Roots”(一組必須活躍的引用)作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索走過的路徑稱為引用鏈,
當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈時(shí)候,那么證明此對(duì)象是不可用的。
在Java語言中,做作為GC Roots的對(duì)象包括以下幾種:
1)虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象。
2)方法區(qū)中類靜態(tài)屬性引用的對(duì)象。
3)方法區(qū)中常量引用的對(duì)象。
4)本地方法棧JNI(即一般說的Native方法)引用的對(duì)象。
無論是通過引用計(jì)數(shù)器算法判斷對(duì)象的引用數(shù)量,還是通過可達(dá)性分析算法判斷對(duì)象引用鏈?zhǔn)欠窨蛇_(dá),判斷對(duì)象是否可活都離不開引用,Java中將引用分為四種:
1)強(qiáng)引用(Strong Reference)
是指程序代碼中普遍存在的,類似“Object obj = new Object()”這類的引用,只有強(qiáng)引用還存在對(duì)象就不會(huì)被回收。
2)軟引用(Soft Reference)
軟引用是用來描述一些還有用但是非必須的對(duì)象。對(duì)于軟引用關(guān)聯(lián)的對(duì)象,在系統(tǒng)將于發(fā)生內(nèi)存溢出異常之前,將會(huì)把這些對(duì)象列進(jìn)回收范圍中進(jìn)行二次回收。
3)弱引用(Weak Reference)
也是用來描述非必須對(duì)象的,強(qiáng)度比軟引用還弱一些,被軟引用關(guān)聯(lián)的對(duì)象只能存活到下一次內(nèi)存回收之前。
4)虛引用(Phantom Referenece)
也成為幽靈引用和幻影引用,為一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個(gè)對(duì)象被回收時(shí)收到一個(gè)系統(tǒng)通知。
即使在可達(dá)性分析算法中不可達(dá)的對(duì)象,也并非一定是“非死不可”的,這時(shí)候他們暫時(shí)處于“緩刑”階段,真正宣告一個(gè)對(duì)象死亡至少要經(jīng)歷兩個(gè)階段:
1)如果對(duì)象在可達(dá)性分析算法中不可達(dá),那么它會(huì)被第一次標(biāo)記并進(jìn)行一次刷選,刷選的條件是是否需要執(zhí)行finalize()方法(當(dāng)對(duì)象沒有覆蓋finalize()或者finalize()方法已經(jīng)執(zhí)行過了(對(duì)象的此方法只會(huì)執(zhí)行一次)),虛擬機(jī)將這兩種情況都會(huì)視為沒有必要執(zhí)行)。
2)如果這個(gè)對(duì)象有必要執(zhí)行finalize()方法會(huì)將其放入F-Queue隊(duì)列中,稍后GC將對(duì)F-Queue隊(duì)列進(jìn)行第二次標(biāo)記,如果在重寫finalize()方法中將對(duì)象自己賦值給某個(gè)類變量或者對(duì)象的成員變量,那么第二次標(biāo)記時(shí)候就會(huì)將它移出“即將回收”的集合。
finalize()能做的工作,使用try-finally或者其他方式都能做到更好,更及時(shí),所以不建議使用此方法。
1)該類所以的實(shí)例都已經(jīng)被回收
2)加載該類的ClassLoader被回收
3)該類對(duì)應(yīng)的java.lang.Class對(duì)象沒有在任何地方引用,無法在任何地方通過反射訪問該類的方法
blog.csdn.net/moHedong/java/article/details/79687878
