1.1 引用計(jì)數(shù)算法
基本思想:
給對(duì)象添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值就加1,當(dāng)引用失效時(shí),計(jì)數(shù)器值就減1,任何時(shí)刻計(jì)數(shù)器為0的對(duì)象就是不可能再被使用的。
優(yōu)點(diǎn):
引用計(jì)數(shù)算法的實(shí)現(xiàn)簡(jiǎn)單,判定效率也很高,在大部分情況下它都是一個(gè)不錯(cuò)的選擇。
缺點(diǎn):
Java虛擬機(jī)并沒有選擇這種算法來進(jìn)行垃圾回收,主要原因是它很難解決對(duì)象之間的相互循環(huán)引用問題。
1.2 可達(dá)性分析算法
基本思想:
通過一系列的稱為“GC Roots”的對(duì)象作為起始點(diǎn),從這些起始點(diǎn)開始向下搜索,所走過的路徑稱為引用鏈,如果一個(gè)對(duì)象到GC Roots沒有任何引用鏈,那么這個(gè)對(duì)象是不可用的,就是說,程序中沒有誰引用了這個(gè)對(duì)象,所以可以說從根節(jié)點(diǎn)到葉子結(jié)點(diǎn)是不可達(dá)的。

在Java語言中,以下對(duì)象可作為GC Roots對(duì)象:
- 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象。
- 方法區(qū)中類靜態(tài)屬性引用的對(duì)象。
- 方法區(qū)中常量引用的對(duì)象。
- 本地方法棧中JNI(也就是native本地方法)引用的對(duì)象。
1.3 對(duì)象finalize()方法的自我拯救
注意,即使在可達(dá)性分析算法中判定為可回收的對(duì)象(不可達(dá)),也并非是“非死不可”的。
原因在于要宣告一個(gè)對(duì)象的死亡,需要兩次標(biāo)記,如果一個(gè)對(duì)象沒有與GC Roots結(jié)點(diǎn)相連,就會(huì)被第一次標(biāo)記,并且進(jìn)行一次篩選:此對(duì)象是否有必要執(zhí)行finalize()方法。如果對(duì)象沒有覆蓋finalize()方法,或者該方法已經(jīng)被JVM調(diào)用過了,則“沒有必要執(zhí)行”finalize()方法。如果對(duì)象覆蓋了finalize方法,并且在finalize方法中與某個(gè)對(duì)象建立了引用關(guān)系例如把this關(guān)鍵字(自己)賦值給某個(gè)類變量或者對(duì)象的成員變量(GC Roots),那么第二次標(biāo)記會(huì)失敗,那么這個(gè)對(duì)象就會(huì)被移出“即將回收”的對(duì)象列表,移出之后這個(gè)對(duì)象就“活”了下來,如果在finalize方法中這個(gè)對(duì)象仍然沒有與一個(gè)對(duì)象建立引用關(guān)系,那么這個(gè)對(duì)象就真正死亡了。
1.4 再談引用
在JDK 1.2之后,Java對(duì)引用的概念進(jìn)行了擴(kuò)充,將引用分為強(qiáng)引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)4種,這4種引用強(qiáng)度依次逐漸減弱。
- 強(qiáng)引用:類似“Object obj = new Object()”這類的引用,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象。
- 軟引用:用來描述一些還有用但并非必需的對(duì)象。對(duì)于軟引用關(guān)聯(lián)著的對(duì)象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會(huì)把這些對(duì)象列進(jìn)回收范圍之中進(jìn)行第二次回收。如果這次回收還沒有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常。在JDK 1.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ì)象。在JDK 1.2之后,提供了WeakReference類來實(shí)現(xiàn)弱引用。
- 虛引用:幽靈引用或者幻影引用,它是最弱的一種引用關(guān)系。一個(gè)對(duì)象是否有虛引用的存在,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響,也無法通過虛引用來取得一個(gè)對(duì)象實(shí)例。為一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個(gè)對(duì)象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知。在JDK 1.2之后,提供了PhantomReference類來實(shí)現(xiàn)虛引用。
1.5 回收方法區(qū)
Java的內(nèi)存回收主要是在方法區(qū)和Java堆中,Java堆中的新生代,因?yàn)樾律拇婊顣r(shí)間比較短,所以對(duì)新生代進(jìn)行垃圾回收的空間比較大,但是方法區(qū)中的永久代則由于可能存活時(shí)間較長(zhǎng),所以下一次的垃圾回收回收該對(duì)象的可能性沒有新生代那么大。所以對(duì)永久代的回收效率會(huì)大打折扣。但是這部分對(duì)象仍然是需要回收。
永久代的垃圾回收包括兩部分:廢棄常量和無用的類
廢棄常量的回收比較好理解,因?yàn)橹灰獩]有任何對(duì)象引用常量池中的某個(gè)對(duì)象,那么這個(gè)對(duì)象就會(huì)被回收。前面說的是非常量池中的對(duì)象,廢棄常量回收的是運(yùn)行時(shí)常量池中的對(duì)象,所以只需要一次標(biāo)記就好。
無用的類回收需要滿足以下三個(gè)條件才可以宣判一個(gè)類的“死刑”:
- 該類的所有實(shí)例都已經(jīng)被回收,也就是Java堆中不存在該類的實(shí)例
- 加載該類的ClassLoader已經(jīng)被回收
- 該類對(duì)應(yīng)的java.lang.Class對(duì)象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法
注意上面的“可以”而非“必然”。與對(duì)象的回收不同,是否需要對(duì)類進(jìn)行回收,需要設(shè)置相關(guān)的參數(shù)才行。
在大量使用反射、動(dòng)態(tài)代理、CGLib等ByteCode框架、動(dòng)態(tài)生成JSP以及OSGi這類頻繁自定義ClassLoader的場(chǎng)景都需要虛擬機(jī)具備類卸載的功能,以保證永久代不會(huì)溢出。