在Java中,雖然不需要程序員手動去管理對象的生命周期
但是如果希望某些對象具備一定的生命周期的話
但是又要避免OOM,比如內(nèi)存不足時(shí)JVM就會自動回收某些對象從而避免OutOfMemory的錯(cuò)誤
當(dāng)遇到這種錯(cuò)誤如何避免,如何有效地解決這個(gè)問題呢?
需要用到軟引用和弱引用了
強(qiáng)引用、軟引用、弱引用、虛引用的概念
從Java SE2開始,就提供了四種類型的引用:強(qiáng)引用、軟引用、弱引用和虛引用
Java中提供這四種引用類型主要有兩個(gè)目的:
第一是可以讓程序員通過代碼的方式?jīng)Q定某些對象的生命周期;
第二是有利于JVM進(jìn)行垃圾回收。

1.強(qiáng)引用(StrongReference)
強(qiáng)引用就是指在程序代碼之中普遍存在的,比如下面這段代碼中的object和str都是強(qiáng)引用:
Object object = new Object();
String str = "hello";
2.軟引用(SoftReference)
軟引用是用來描述一些有用但并不是必需的對象
在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會把這些對象列進(jìn)回收范圍進(jìn)行二次回收。如果這次回收還沒有足夠的內(nèi)存,才會拋出內(nèi)存溢出異常
在Java中用java.lang.ref.SoftReference類來表示
對于軟引用關(guān)聯(lián)著的對象,只有在內(nèi)存不足的時(shí)候JVM才會回收該對象
因此,這一點(diǎn)可以很好地用來解決OOM的問題
并且這個(gè)特性很適合用來實(shí)現(xiàn)緩存:比如網(wǎng)頁緩存、圖片緩存等
3.弱引用(WeakReference)
弱引用也是用來描述非必需對象的
當(dāng)JVM進(jìn)行垃圾回收時(shí),無論內(nèi)存是否充足,都會回收被弱引用關(guān)聯(lián)的對象
在java中,用java.lang.ref.WeakReference類來表示
4.虛引用(PhantomReference)
虛引用和前面的軟引用、弱引用不同,它并不影響對象的生命周期
在java中用java.lang.ref.PhantomReference類表示
如果一個(gè)對象與虛引用關(guān)聯(lián),則跟沒有引用與之關(guān)聯(lián)一樣,在任何時(shí)候都可能被垃圾回收器回收
垃圾回收原理分析
為什么要進(jìn)行垃圾回收?
前面已經(jīng)說過了有OOM的問題,因?yàn)閮?nèi)存不是無限大的
而我們一直在占用內(nèi)存,而不釋放那么總有某個(gè)時(shí)候內(nèi)存會被消耗完
所以必須要進(jìn)行垃圾回收
那什么樣的對象可以當(dāng)作是垃圾?垃圾什么時(shí)候被回收?
垃圾我們的理解就是沒有用處的了,沒有被使用的對象
那么如何找到這些對象
判斷對象是否是垃圾有兩種算法:
- 引用計(jì)數(shù)法
給對象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)一個(gè)地方引用這個(gè)對象時(shí),計(jì)數(shù)器值+1;當(dāng)引用失效時(shí),計(jì)數(shù)器值-1。任何時(shí)刻計(jì)數(shù)值為0的對象就是不可能再被使用的
Java中卻沒有使用這種算法,因?yàn)檫@種算法很難解決對象之間相互引用的情況
- GC Roots可達(dá)分析算法
通過一系列稱為“GC Roots”的對象作為起始點(diǎn),從這些節(jié)點(diǎn)向下搜索,搜索所走過的路徑稱為引用鏈,當(dāng)一個(gè)對象到GC Roots沒有任何引用鏈(即GC Roots到對象不可達(dá))時(shí),則證明此對象是不可用的
回收分為兩步:
- 將需要回收的對象加入回收隊(duì)列
- 調(diào)用對象的 finalize() 方法
GC Roots不可達(dá)的對象并非是“非死不可”的
若要宣判一個(gè)對象死亡,至少需要經(jīng)歷兩次標(biāo)記階段
如果對象在finalize方法中拯救了自己,即關(guān)聯(lián)上了GCRoots引用鏈,如把this關(guān)鍵字賦值給其他變量,將會從回收隊(duì)列中移除
如何判斷無用的類呢?需要滿足以下三個(gè)條件
該類的所有實(shí)例都已經(jīng)被回收,即Java堆中不存在該類的任何實(shí)例。
加載該類的ClassLoader已經(jīng)被回收。
該類對應(yīng)的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。
回收算法有:
- 標(biāo)記清除算法
效率低,節(jié)省一些內(nèi)存
標(biāo)記清除后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片, 內(nèi)存碎片太多可能會導(dǎo)致以后程序運(yùn)行過程中在需要分配較大對象時(shí),無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)一次垃圾收集動作
- 復(fù)制算法
效率要高,浪費(fèi)一些內(nèi)存
它將可用的內(nèi)存分為兩塊,每次只用其中一塊,當(dāng)這一塊內(nèi)存用完了,就將還存活著的對象復(fù)制到另外一塊上面,然后再把已經(jīng)使用過的內(nèi)存空間一次性清理掉
標(biāo)記整理算法
讓所有存活對象都向一端移動,然后直接清理掉邊界以外的內(nèi)存分代收集算法
設(shè)計(jì)GC分代,對象有年齡,新生代,老年代,永久代,不同代算法不同
根據(jù)對象的生命周期的不同將內(nèi)存劃分為幾塊,然后根據(jù)各塊的特點(diǎn)采用最適當(dāng)?shù)氖占惴?/p>
沒有最好的垃圾收集器,更加沒有萬能的收集器,只能選擇對具體應(yīng)用最合適的收集器