java中的4种reference的差别和使用场景(含理论、代码和执行结果)
轉(zhuǎn):https://blog.csdn.net/aitangyong/article/details/39453365
我們知道java語(yǔ)言提供了4種引用類型:強(qiáng)引用、軟引用(SoftReference)、弱引用(WeakReference)和幽靈引用(PhantomReference),與引用密切相關(guān)的,還有一個(gè)引用隊(duì)列ReferenceQueue。引用和引用隊(duì)列的關(guān)系,對(duì)于垃圾回收來(lái)說(shuō)非常重要,學(xué)習(xí)垃圾回收機(jī)制,必須要先了解引用和引用隊(duì)列的使用方法。本文主要參考網(wǎng)上的一些理論,同時(shí)配合自己的一些測(cè)試代碼,更好的理解這些概念。這篇博客也解決了?System.gc()和-XX:+DisableExplicitGC啟動(dòng)參數(shù),以及DirectByteBuffer的內(nèi)存釋放??中遺留的幽靈引用的問(wèn)題。
1、強(qiáng)引用
? ? ?強(qiáng)引用不會(huì)被GC回收,并且在java.lang.ref里也沒(méi)有實(shí)際的對(duì)應(yīng)類型,平時(shí)工作接觸的最多的就是強(qiáng)引用。
Object obj = new Object();這里的obj引用便是一個(gè)強(qiáng)引用。如果一個(gè)對(duì)象具有強(qiáng)引用,那就類似于必不可少的生活用品,垃圾回收器絕不會(huì)回收它。當(dāng)內(nèi)存空 間不足,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯(cuò)誤,使程序異常終止,也不會(huì)靠隨意回收具有強(qiáng)引用的對(duì)象來(lái)解決內(nèi)存不足問(wèn)題。
2、軟引用
如果一個(gè)對(duì)象只具有軟引用,那就類似于可有可物的生活用品。如果內(nèi)存空間足夠,垃圾回收器就不會(huì)回收它,如果內(nèi)存空間不足了,就會(huì)回收這些對(duì)象的內(nèi)存。只 要垃圾回收器沒(méi)有回收它,該對(duì)象就可以被程序使用。軟引用可用來(lái)實(shí)現(xiàn)內(nèi)存敏感的高速緩存。 軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果軟引用所引用的對(duì)象被垃圾回收,Java虛擬機(jī)就會(huì)把這個(gè)軟引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。
?
這里有幾點(diǎn)需要說(shuō)明:
1、System.gc()告訴JVM這是一個(gè)執(zhí)行GC的好時(shí)機(jī),但具體執(zhí)不執(zhí)行由JVM決定(事實(shí)上這段代碼一般都會(huì)執(zhí)行GC)
2、Thread.sleep(200); 這是因?yàn)閺膶?duì)象被回收到JVM將引用加入refQueue隊(duì)列,需要一定的時(shí)間。而且poll并不是一個(gè)阻塞方法,如果沒(méi)有數(shù)據(jù)會(huì)返回null,所以我們選擇等待一段時(shí)間。
3、弱引用
如果一個(gè)對(duì)象只具有弱引用,那就類似于可有可物的生活用品。弱引用與軟引用的區(qū)別在于:只具有弱引用的對(duì)象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過(guò)程中,一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存。不過(guò),由于垃圾回收器是一個(gè)優(yōu)先級(jí)很低的線程, 因此不一定會(huì)很快發(fā)現(xiàn)那些只具有弱引用的對(duì)象。 ?弱引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果弱引用所引用的對(duì)象被垃圾回 收,Java虛擬機(jī)就會(huì)把這個(gè)弱引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。?
/**
* 弱引用: 當(dāng)發(fā)生GC的時(shí)候,Weak引用對(duì)象總是會(huì)內(nèi)回收回收。因此Weak引用對(duì)象會(huì)更容易、更快被GC回收。
* Weak引用對(duì)象常常用于Map數(shù)據(jù)結(jié)構(gòu)中,引用占用內(nèi)存空間較大的對(duì)象
*
* <pre>
* 如果不發(fā)生垃圾回收:
* java.lang.Object@f9f9d8
* null
* java.lang.Object@f9f9d8
* null
*
* 如果發(fā)生垃圾回收:
* java.lang.Object@f9f9d8
* null
* null
* java.lang.ref.WeakReference@422ede
*
* <pre>
*/
public static void weak() throws Exception
{
Object obj = new Object();
ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
WeakReference<Object> weakRef = new WeakReference<Object>(obj, refQueue);
System.out.println(weakRef.get()); // java.lang.Object@f9f9d8
System.out.println(refQueue.poll());// null
?
// 清除強(qiáng)引用,觸發(fā)GC
obj = null;
System.gc();
?
System.out.println(weakRef.get());
?
// 這里特別注意:poll是非阻塞的,remove是阻塞的.
// JVM將弱引用放入引用隊(duì)列需要一定的時(shí)間,所以這里先睡眠一會(huì)兒
// System.out.println(refQueue.poll());// 這里有可能是null
?
Thread.sleep(200);
System.out.println(refQueue.poll());
// System.out.println(refQueue.poll());//這里一定是null,因?yàn)橐呀?jīng)從隊(duì)列中移除
?
// System.out.println(refQueue.remove());
}
這里需要注意下:
1、remove這是一個(gè)阻塞方法,類似于J.U.C并發(fā)包下的阻塞隊(duì)列,如果沒(méi)有隊(duì)列沒(méi)有數(shù)據(jù),那么當(dāng)前線程一直等待。
2、如果隊(duì)列有數(shù)據(jù),那么remove和pool都會(huì)將第一個(gè)元素出隊(duì)。
4、幽靈引用(虛引用)
虛引用主要用來(lái)跟蹤對(duì)象被垃圾回收器回收的活動(dòng)。虛引用與軟引用和弱引用的一個(gè)區(qū)別在于:虛引用必須和引用隊(duì)列 (ReferenceQueue)聯(lián)合使用。當(dāng)垃圾回收器回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還有虛引用,就會(huì)把這個(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)。由于Object.finalize()方法的不安全性、低效性,常常使用虛引用完成對(duì)象回收前的資源釋放工作。參考我的另一篇博客:解釋為什么finalize是不安全的,不建議使用
?/**
* 當(dāng)GC一但發(fā)現(xiàn)了虛引用對(duì)象,將會(huì)將PhantomReference對(duì)象插入ReferenceQueue隊(duì)列.
* 而此時(shí)PhantomReference所指向的對(duì)象并沒(méi)有被GC回收,而是要等到ReferenceQueue被你真正的處理后才會(huì)被回收.
*
* <pre>
* 不發(fā)生GC執(zhí)行結(jié)果是:
* null
* null
* null
* null
*
* 發(fā)生GC執(zhí)行結(jié)果是:
* null
* null
* null
* java.lang.ref.PhantomReference@87816d
* </pre>
*
* 虛引用在實(shí)現(xiàn)一個(gè)對(duì)象被回收之前必須做清理操作是很有用的,比f(wàn)inalize()方法更靈活
*/
public static void phantom() throws Exception
{
Object obj = new Object();
ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
PhantomReference<Object> phantom = new PhantomReference<Object>(obj,
refQueue);
System.out.println(phantom.get()); // java.lang.Object@f9f9d8
System.out.println(refQueue.poll());// null
?
obj = null;
System.gc();
?
// 調(diào)用phanRef.get()不管在什么情況下會(huì)一直返回null
System.out.println(phantom.get());
?
// 當(dāng)GC發(fā)現(xiàn)了虛引用,GC會(huì)將phanRef插入進(jìn)我們之前創(chuàng)建時(shí)傳入的refQueue隊(duì)列
// 注意,此時(shí)phanRef所引用的obj對(duì)象,并沒(méi)有被GC回收,在我們顯式地調(diào)用refQueue.poll返回phanRef之后
// 當(dāng)GC第二次發(fā)現(xiàn)虛引用,而此時(shí)JVM將phanRef插入到refQueue會(huì)插入失敗,此時(shí)GC才會(huì)對(duì)obj進(jìn)行回收
Thread.sleep(200);
System.out.println(refQueue.poll());
}
這里特別需要注意:當(dāng)JVM將虛引用插入到引用隊(duì)列的時(shí)候,虛引用執(zhí)行的對(duì)象內(nèi)存還是存在的。但是PhantomReference并沒(méi)有暴露API返回對(duì)象。如NIO直接內(nèi)存的自動(dòng)回收,就使用到了sun.misc.Cleaner
?
public class Cleaner extends PhantomReference
{
}
JDK底層源碼查詢網(wǎng)站:http://www.docjar.com/html/api/sun/misc/Cleaner.java.html
5、小結(jié)
引用和引用隊(duì)列提供了一種通知機(jī)制,允許我們知道對(duì)象已經(jīng)被銷毀或者即將被銷毀。GC要回收一個(gè)對(duì)象的時(shí)候,如果發(fā)現(xiàn)該對(duì)象有軟、弱、虛引用的時(shí)候,會(huì)將這些引用加入到注冊(cè)的引用隊(duì)列中。軟引用和弱引用差別不大,JVM都是先把SoftReference和WeakReference中的referent字段值設(shè)置成null,之后加入到引用隊(duì)列;而虛引用則不同,如果某個(gè)堆中的對(duì)象,只有虛引用,那么JVM會(huì)將PhantomReference加入到引用隊(duì)列中,JVM不會(huì)自動(dòng)將referent字段值設(shè)置成null。
參考資料:
Java:對(duì)象的強(qiáng)、軟、弱和虛引用 ? ?http://zhangjunhd.blog.51cto.com/113473/53092/
Java 中的 Reference ? ? ? ? ? ? ?http://www.cnblogs.com/newcj/archive/2011/05/15/2046882.html
總結(jié)
以上是生活随笔為你收集整理的java中的4种reference的差别和使用场景(含理论、代码和执行结果)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java 图表 word_java Fr
- 下一篇: 解决Stata 15 的中文乱码问题