JVM 四種引用
1. 前言
延續(xù)上節(jié)可達(dá)性分析法的講解,本節(jié)主要講解可達(dá)性分析法所使用的四種引用類(lèi)型,本節(jié)主要內(nèi)容如下:
- 強(qiáng)引用的定義以及如何消除強(qiáng)引用,為本節(jié)重點(diǎn)內(nèi)容之一;
- 軟引用的定義及使用場(chǎng)景,為本節(jié)重點(diǎn)內(nèi)容之一;
- 弱引用的定義及代碼示例,驗(yàn)證任何情況下,只要發(fā)生 GC 就會(huì)回收弱引用對(duì)象,為本節(jié)重點(diǎn)內(nèi)容之一;
- 虛引用的定義以及作用,為本節(jié)重點(diǎn)內(nèi)容之一;
2. 可達(dá)性分析的四種引用類(lèi)型
上節(jié)課程內(nèi)容講解了可達(dá)性分析,可達(dá)性分析的 GC Roots 均為引用對(duì)象,那么引用對(duì)象有 4 種引用類(lèi)型如下:
- 強(qiáng)引用;
- 軟引用;
- 弱引用;
- 虛引用。
本節(jié)課程內(nèi)容與可達(dá)性分析相輔相成,學(xué)習(xí)者務(wù)必在學(xué)習(xí)完可達(dá)性分析內(nèi)容后再學(xué)習(xí)本節(jié)內(nèi)容。
3. 強(qiáng)引用
定義:強(qiáng)引用就是指在程序代碼之中普遍存在的,類(lèi)似Object obj = new Object()
這類(lèi)的引用,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象。當(dāng)內(nèi)存空間不足,Java 虛擬機(jī)寧愿拋出 OutOfMemoryError 錯(cuò)誤,使程序異常終止,也不會(huì)靠隨意回收具有強(qiáng)引用的對(duì)象來(lái)解決內(nèi)存不足的問(wèn)題。
代碼示例:
public class DemoTest {
public static void main(String[] args) {
Object obj = new Object(); // 強(qiáng)引用
}
}
在強(qiáng)引用的定義中有這樣一句話:“只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象?!?那么有沒(méi)有辦法將強(qiáng)引用消除呢?
消除強(qiáng)引用示例代碼:
public class DemoTest {
public static void main(String[] args) {
Object obj = new Object(); // 強(qiáng)引用
obj = null; //消除強(qiáng)引用
}
}
如果不使用強(qiáng)引用時(shí),可以賦值 obj=null
,顯示的設(shè)置 obj 為 null,則 gc 認(rèn)為該對(duì)象不存在引用,這時(shí)候就可以回收此對(duì)象。
4. 軟引用
定義:軟引用用來(lái)描述一些還有用,但并非必需的對(duì)象。對(duì)于軟引用關(guān)聯(lián)著的對(duì)象,如果內(nèi)存充足,則垃圾回收器不會(huì)回收該對(duì)象,如果內(nèi)存不夠了,就會(huì)回收這些對(duì)象的內(nèi)存。
在 JDK 1.2 之后,提供了 SoftReference 類(lèi)來(lái)實(shí)現(xiàn)軟引用。軟引用可用來(lái)實(shí)現(xiàn)內(nèi)存敏感的高速緩存。軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果軟引用所引用的對(duì)象被垃圾回收器回收,Java虛擬機(jī)就會(huì)把這個(gè)軟引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。
軟引用使用場(chǎng)景:Android 應(yīng)用圖片
軟引用主要應(yīng)用于內(nèi)存敏感的高速緩存,在 Android 系統(tǒng)中經(jīng)常使用到。一般情況下,Android 應(yīng)用會(huì)用到大量的默認(rèn)圖片,這些圖片很多地方會(huì)用到。如果每次都去讀取圖片,由于讀取文件需要硬件操作,速度較慢,會(huì)導(dǎo)致性能較低。所以我們考慮將圖片緩存起來(lái),需要的時(shí)候直接從內(nèi)存中讀取。
但是,由于圖片占用內(nèi)存空間比較大,緩存很多圖片需要很多的內(nèi)存,就可能比較容易發(fā)生 OutOfMemory 異常。這時(shí),我們可以考慮使用軟引用技術(shù)來(lái)避免這個(gè)問(wèn)題發(fā)生。
SoftReference 可以解決 OOM 的問(wèn)題,每一個(gè)對(duì)象通過(guò)軟引用進(jìn)行實(shí)例化,這個(gè)對(duì)象就以cache的形式保存起來(lái),當(dāng)再次調(diào)用這個(gè)對(duì)象時(shí),那么直接通過(guò)軟引用中的 get() 方法,就可以得到對(duì)象中的資源數(shù)據(jù),這樣就沒(méi)必要再次進(jìn)行讀取了,直接從 cache 中就可以讀取得到,當(dāng)內(nèi)存將要發(fā)生 OOM 的時(shí)候,GC 會(huì)迅速把所有的軟引用清除,防止 OOM 發(fā)生。
5. 弱引用
定義:弱引用描述非必需對(duì)象。被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾回收之前,垃圾收集器工作之后,無(wú)論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象。Java 中的類(lèi) WeakReference 表示弱引用。
代碼示例:
import java.lang.ref.WeakReference;
public class Main {
public static void main(String[] args) {
WeakReference<String> sr = new WeakReference<String>(new String("hello"));
System.out.println(sr.get());
System.gc(); //通知JVM的gc進(jìn)行垃圾回收
System.out.println(sr.get());
}
}
結(jié)果驗(yàn)證:第二個(gè)輸出結(jié)果是 null,這說(shuō)明只要 JVM 進(jìn)行垃圾回收,被弱引用關(guān)聯(lián)的對(duì)象必定會(huì)被回收掉。
hello
null
6. 虛引用
定義:"虛引用"顧名思義,就是形同虛設(shè),與其他幾種引用都不同,虛引用并不會(huì)決定對(duì)象的生命周期。如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒(méi)有任何引用一樣,在任何時(shí)候都可能被垃圾回收。虛引用在 Java 中使用 java.lang.ref.PhantomReference
類(lèi)表示。
作用:虛引用主要用來(lái)跟蹤對(duì)象被垃圾回收的活動(dòng)。
虛引用與軟引用和弱引用的區(qū)別:虛引用必須和引用隊(duì)列(ReferenceQueue)聯(lián)合使用。當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還有虛引用,就會(huì)在回收對(duì)象的內(nèi)存之前,把這個(gè)虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。程序可以通過(guò)判斷引用隊(duì)列中是否已經(jīng)加入了虛引用,來(lái)了解被引用的對(duì)象是否將要被垃圾回收。程序如果發(fā)現(xiàn)某個(gè)虛引用已經(jīng)被加入到引用隊(duì)列,那么就可以在所引用的對(duì)象的內(nèi)存被回收之前采取必要的行動(dòng)。
使用示例:虛引用必須和引用隊(duì)列(ReferenceQueue)聯(lián)合使用
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class Main {
public static void main(String[] args) {
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
System.out.println(pr.get());
}
}
7. 小結(jié)
本節(jié)主要講解可達(dá)性分析的四種對(duì)象引用類(lèi)型,通篇皆為重點(diǎn)內(nèi)容,需要學(xué)習(xí)者理解并掌握這四種引用類(lèi)型。