一. java 4種引用類型介紹
- StrongReference:強(qiáng)引用,不會(huì)被回收,寧肯OOM也不被回收,除非對(duì)象為null的時(shí)候GC才回收
- SoftReference: 軟引用在內(nèi)存不夠的時(shí)候才回收,也就是說正常情況下GC不回收(適合做高速緩存,比如Caffeine內(nèi)存淘汰策略)
- WeakReference: 弱引用,GC碰到直接回收,防止內(nèi)存泄漏(適合做規(guī)范化映射)
- PhantomReference: 虛引用,信號(hào)的作用,回收的時(shí)候給一個(gè)信號(hào)放到隊(duì)列里面,清理直接內(nèi)存,GC回收不回收都獲取不到.(Nio、Netty)直接管理堆外內(nèi)存。(適合做對(duì)象死前所做的清理操作)
- 四種引用的強(qiáng)弱關(guān)系: StrongReference > SoftReference > WeakReference > PhantomReference
二.使用場(chǎng)景
2.1 強(qiáng)引用
Java中默認(rèn)聲明的就是強(qiáng)引用,比如:
Object obj = new Object(); //只要obj還指向Object對(duì)象,Object對(duì)象就不會(huì)被回收
obj = null; //手動(dòng)置null
只要強(qiáng)引用存在,垃圾回收器將永遠(yuǎn)不會(huì)回收被引用的對(duì)象,即使內(nèi)存不足時(shí)也不會(huì)去回收,JVM會(huì)直接拋出OutOfMemoryError。如果想中斷強(qiáng)引用與對(duì)象之間的聯(lián)系,可以顯示的將強(qiáng)引用賦值為null,這樣一來,JVM就可以適時(shí)的回收對(duì)象了
2.2 軟引用
軟引用對(duì)象是在jvm內(nèi)存不夠的時(shí)候才會(huì)被回收,我們調(diào)用System.gc()方法只是起通知作用,JVM什么時(shí)候掃描回收對(duì)象是JVM自己的狀態(tài)決定的。就算掃描到軟引用對(duì)象也不一定會(huì)回收它,軟引用發(fā)生的時(shí)機(jī)是在OOM時(shí)。
軟引用可用來實(shí)現(xiàn)內(nèi)存敏感的高速緩存。 軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用。如果軟引用所引用對(duì)象被垃圾回收,JAVA虛擬機(jī)就會(huì)把這個(gè)軟引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。
ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
String str = new String("abc");
SoftReference<String> softReference = new SoftReference<>(str, referenceQueue);
str = null;
// Notify GC
System.gc();
System.out.println(softReference.get()); // abc
Reference<? extends String> reference = referenceQueue.poll();
System.out.println(reference); //null
當(dāng)內(nèi)存不足時(shí),JVM首先將軟引用中的對(duì)象引用置為null,然后通知垃圾回收器進(jìn)行回收:
if(JVM內(nèi)存不足) {
// 將軟引用中的對(duì)象引用置為null
str = null;
// 通知垃圾回收器進(jìn)行回收
System.gc();
}
也就是說,垃圾收集線程會(huì)在虛擬機(jī)拋出OutOfMemoryError之前回收軟引用對(duì)象,而且虛擬機(jī)會(huì)盡可能優(yōu)先回收長(zhǎng)時(shí)間閑置不用的軟引用對(duì)象。對(duì)那些剛構(gòu)建的或剛使用過的“較新的”軟對(duì)象會(huì)被虛擬機(jī)盡可能保留,這就是引入引用隊(duì)列ReferenceQueue的原因
應(yīng)用場(chǎng)景:
瀏覽器的后退按鈕。按后退時(shí),這個(gè)后退時(shí)顯示的網(wǎng)頁內(nèi)容是重新進(jìn)行請(qǐng)求還是從緩存中取出呢?這就要看具體的實(shí)現(xiàn)策略了。
- 如果一個(gè)網(wǎng)頁在瀏覽結(jié)束時(shí)就進(jìn)行內(nèi)容的回收,則按后退查看前面瀏覽過的頁面時(shí),需要重新構(gòu)建;
- 如果將瀏覽過的網(wǎng)頁存儲(chǔ)到內(nèi)存中會(huì)造成內(nèi)存的大量浪費(fèi),甚至?xí)斐蓛?nèi)存溢出。
// 獲取瀏覽器對(duì)象進(jìn)行瀏覽
Browser browser = new Browser();
// 從后臺(tái)程序加載瀏覽頁面
BrowserPage page = browser.getPage();
// 將瀏覽完畢的頁面置為軟引用
SoftReference softReference = new SoftReference(page);
// 回退或者再次瀏覽此頁面時(shí)
if(softReference.get() != null) {
// 內(nèi)存充足,還沒有被回收器回收,直接獲取緩存
page = softReference.get();
} else {
// 內(nèi)存不足,軟引用的對(duì)象已經(jīng)回收
page = browser.getPage();
// 重新構(gòu)建軟引用
softReference = new SoftReference(page);
}
2.3 弱引用
具有弱引用的對(duì)象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存。不過,由于垃圾回收器是一個(gè)優(yōu)先級(jí)很低的線程,因此不一定會(huì)很快發(fā)現(xiàn)那些只具有弱引用的對(duì)象。
WeakReference對(duì)象的生命周期基本由垃圾回收器決定,一旦垃圾回收線程發(fā)現(xiàn)了弱引用對(duì)象,在下一次GC過程中就會(huì)對(duì)其進(jìn)行回收。
String str = new String("abc");
WeakReference<String> weakReference = new WeakReference<>(str);
str = null;
JVM首先將軟引用中的對(duì)象引用置為null,然后通知垃圾回收器進(jìn)行回收:
str = null;
System.gc();
注意:如果一個(gè)對(duì)象是偶爾(很少)的使用,并且希望在使用時(shí)隨時(shí)就能獲取到,但又不想影響此對(duì)象的垃圾收集,那么你應(yīng)該用Weak Reference來記住此對(duì)象。
2.4 虛引用
虛引用顧名思義,就是形同虛設(shè)。與其他幾種引用都不同,虛引用并不會(huì)決定對(duì)象的生命周期。如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時(shí)候都可能被垃圾回收器回收。
應(yīng)用場(chǎng)景:
- 虛引用主要用來跟蹤對(duì)象被垃圾回收器回收的活動(dòng)。
- 虛引用與軟引用和弱引用的一個(gè)區(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ì)列中。PhantomReference只有一個(gè)構(gòu)造函數(shù)和一個(gè) get() 方法,而且它的 get() 方法僅僅是返回一個(gè)null,也就是說將永遠(yuǎn)無法通過虛引用來獲取對(duì)象。
String str = new String("abc");
ReferenceQueue queue = new ReferenceQueue();
// 創(chuàng)建虛引用,要求必須與一個(gè)引用隊(duì)列關(guān)聯(lián)
PhantomReference pr = new PhantomReference(str, queue);
程序可以通過判斷引用隊(duì)列中是否已經(jīng)加入了虛引用,來了解被引用的對(duì)象是否將要進(jìn)行垃圾回收。如果程序發(fā)現(xiàn)某個(gè)虛引用已經(jīng)被加入到引用隊(duì)列,那么就可以在所引用的對(duì)象的內(nèi)存被回收之前采取必要的行動(dòng)。