目錄:
1、怎樣判斷一個(gè)對(duì)象“已死”?
2、引用的分類
3、回收方法區(qū)的數(shù)據(jù)
1、怎樣判斷一個(gè)對(duì)象“已死”?
在堆里面存放著 Java 世界中幾乎所有的對(duì)象實(shí)例,垃圾收集器在對(duì)堆進(jìn)行回收前,第一件事情就是要確定這些對(duì)象之中哪些還“存活”著,哪些已經(jīng)“死去”(即不可能再被任何途徑使用的對(duì)象)。
那么怎么判斷一個(gè)對(duì)象“已死”呢,目前有兩種算法可以判斷對(duì)象“已死”。
-
引用計(jì)數(shù)算法:
舉個(gè)簡單的例子,請(qǐng)看下面代碼中的 testGC() 方法:對(duì)象 objA 和 objB 都有字段 mInstance,賦值令 objA.mInstance = objB 及 objB.mInstance = objA,除此之外,這兩個(gè)對(duì)象再無任何實(shí)際上這兩個(gè)對(duì)象已經(jīng)不可能再被訪問,但是它們因?yàn)榛ハ嘁弥鴮?duì)方,導(dǎo)致它們的引用計(jì)數(shù)都不為 0,于是引用技術(shù)算法無法通知 GC 收集器回收它們。
這個(gè)算法的判斷依據(jù)是通過給對(duì)象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值就加 1;當(dāng)引用失效時(shí),計(jì)數(shù)器值就減 1;任何時(shí)刻計(jì)數(shù)器為 0 的對(duì)象就不可能再被使用的。
客觀的說,引用計(jì)數(shù)算法的實(shí)現(xiàn)簡單,判斷效率也很高,在大部分情況下它都是一個(gè)不錯(cuò)的算法,也有一些比較著名的應(yīng)用案例,例如微軟公司的 COM(Component Object Model)技術(shù)。但是,至少主流的 Java 虛擬機(jī)里面沒有選用引用計(jì)算法來管理內(nèi)存,其中最主要的原因是它很難解決對(duì)象之間相互循環(huán)引用的問題。
對(duì)象不存在時(shí)引用計(jì)數(shù)器不為 0 的情況 -
可達(dá)性分析算法:
在主流的商用程序語言(如Java)的主流實(shí)現(xiàn)中,都是稱通過可達(dá)性分析來判斷對(duì)象是否存活的。這個(gè)算法的基本思路就是通過一系列的稱為“GC Roots”的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過的路徑稱為引用鏈,當(dāng)一個(gè)對(duì)象到 GC Roots 沒有任何引用鏈相連時(shí),則證明此對(duì)象是不可用的。
可達(dá)性分析算法判定對(duì)象是否可回收
在 Java 語言中,可作為 GC Roots 的對(duì)象包括下面幾種:
- 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象。
- 方法區(qū)中類靜態(tài)屬性引用的對(duì)象。
- 方法區(qū)中常量引用的對(duì)象。
- 本地方法棧中 JNI(即一般說的 Native 方法)引用的對(duì)象。
2、引用的分類
無論是通過引用計(jì)數(shù)算法判斷對(duì)象的引用數(shù)量,還是通過可達(dá)性分析判斷對(duì)象的引用鏈?zhǔn)欠窨蛇_(dá),判斷對(duì)象是否存活都與“引用”有關(guān)。
在 JDK 1.2 之后,Java 對(duì)引用的概念進(jìn)行了擴(kuò)充,將引用分為強(qiáng)引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference) 4 種,這 4 種引用強(qiáng)度一次逐漸減弱。
- 強(qiáng)引用就是指在程序代碼之中普遍存在的,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象。
- 軟引用是用來描述一些還有用但并非必須的對(duì)象。對(duì)于軟引用關(guān)聯(lián)著的對(duì)象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前(即內(nèi)存緊張), 將會(huì)把這些對(duì)象列進(jìn)垃圾回收范圍之中進(jìn)行第二次回收。如果這次回收還沒有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常。在 JDK1.2 之后, 提供了 SoftReference 類累實(shí)現(xiàn)軟引用。
- 弱引用是用來描述非必需的對(duì)象的,但是它的強(qiáng)度比軟引用更弱一些,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾收集發(fā)生之前。 當(dāng)垃圾收集器工作時(shí),無論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象。使用 WeekReference 類來實(shí)現(xiàn)弱引用。
- 虛引用也成為幽靈引用或者幻影引用,它是最弱的一種引用。一個(gè)對(duì)象是否有虛引用的存在,完全不會(huì)對(duì)其生存周期時(shí)間構(gòu)成影響, 也無法通過虛引用來取得一個(gè)對(duì)象實(shí)例。唯一的作用就是能在這個(gè)對(duì)象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知。使用 PhantomReference 表示。
3、回收方法區(qū)數(shù)據(jù)
方法區(qū)的垃圾收集主要回收兩部分內(nèi)容:廢棄常量和無用的類?;厥諒U棄常量與回收 Java 堆中的對(duì)象非常類似。以常量池中字面量的回收為例,假如一個(gè)字符串“abc”已經(jīng)進(jìn)入了常量池,但是當(dāng)前系統(tǒng)沒有任何一個(gè) String 對(duì)象是叫做 “abc”的,換句話說,就是沒有任何一個(gè) String 對(duì)象引用常量池中的“abc”常量,也沒有其他地方引用了這個(gè)字面量,如果這時(shí)發(fā)生內(nèi)存回收,而且必要的話,這個(gè)“abc”常量就會(huì)被系統(tǒng)清理出常量池。常量池中的其他類(接口)、方法、字段的符號(hào)引用也與此類似。
判定一個(gè)常量為“廢棄常量”比較簡單,而要判斷一個(gè)常量池中的類是否是“無用的類”條件則苛刻很多。類需要同時(shí)滿足下面 3 個(gè)條件才能稱為“無用的類”:
- 該類所有的實(shí)例都已經(jīng)被全部回收,也就是說 Java 堆中不再存在任何該類的實(shí)例。
- 加載該類的 ClassLoader 已經(jīng)被回收。
- 該類對(duì)應(yīng)的 java.lang.Class 對(duì)象沒有在任何地方引用,并且無法再任何地方通過反射調(diào)用該類的方法。
最后做個(gè)總結(jié):
- 我們可以通過 引用計(jì)數(shù)器 和 可達(dá)性算法 來判斷一個(gè)對(duì)象是否“已死”。引用計(jì)數(shù)器很難解決對(duì)象之間互相循環(huán)引用的問題,所以在主流的商用程序語言(如Java)的主流實(shí)現(xiàn)中,都是稱通過可達(dá)性分析來判斷對(duì)象是否存活的。
- 對(duì)象的引用可以分為 強(qiáng)引用、軟引用、弱引用 以及 虛引用 4 種,其中被 強(qiáng)引用 引用的對(duì)象垃圾收集器永遠(yuǎn)不會(huì)回收掉;被 軟引用 引用的對(duì)象,只有當(dāng)系統(tǒng)將要發(fā)生內(nèi)存溢出時(shí),才會(huì)去回收軟引用引用的對(duì)象;只被 弱引用關(guān)聯(lián)的對(duì)象,只要發(fā)生垃圾收集事件,只被弱引用關(guān)聯(lián)的對(duì)象就會(huì)被回收;被虛引用關(guān)聯(lián)的對(duì)象的唯一作用是能在這個(gè)對(duì)象被回收器回收時(shí)受到一個(gè)系統(tǒng)通知。
- 回收方法區(qū)的數(shù)據(jù),垃圾收集器主要回收 廢棄常量和無用的類 兩部分內(nèi)容。

