四種引用類型
為東港等地區(qū)用戶提供了全套網(wǎng)頁設計制作服務,及東港網(wǎng)站建設行業(yè)解決方案。主營業(yè)務為成都網(wǎng)站建設、做網(wǎng)站、東港網(wǎng)站設計,以傳統(tǒng)方式定制建設網(wǎng)站,并提供域名空間備案等一條龍服務,秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務。我們深信只要達到每一位用戶的要求,就會得到認可,從而選擇與我們長期合作。這樣,我們也可以走得更遠!
所以在 JDK.1.2 之后,Java 對引用的概念進行了擴充,將引用分為了:強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)4 種,這 4 種引用的強度依次減弱。
一,強引用
Java中默認聲明的就是強引用,比如:
Object obj = new Object(); //只要obj還指向Object對象,Object對象就不會被回收
obj = null; //手動置null
只要強引用存在,垃圾回收器將永遠不會回收被引用的對象,哪怕內(nèi)存不足時,JVM也會直接拋出OutOfMemoryError,不會去回收。如果想中斷強引用與對象之間的聯(lián)系,可以顯示的將強引用賦值為null,這樣一來,JVM就可以適時的回收對象了
二,軟引用
軟引用是用來描述一些非必需但仍有用的對象。在內(nèi)存足夠的時候,軟引用對象不會被回收,只有在內(nèi)存不足時,系統(tǒng)則會回收軟引用對象,如果回收了軟引用對象之后仍然沒有足夠的內(nèi)存,才會拋出內(nèi)存溢出異常。這種特性常常被用來實現(xiàn)緩存技術,比如網(wǎng)頁緩存,圖片緩存等。
在 JDK1.2 之后,用java.lang.ref.SoftReference類來表示軟引用。
下面以一個例子來進一步說明強引用和軟引用的區(qū)別:
在運行下面的Java代碼之前,需要先配置參數(shù) -Xms2M -Xmx3M,將 JVM 的初始內(nèi)存設為2M,最大可用內(nèi)存為 3M。
首先先來測試一下強引用,在限制了 JVM 內(nèi)存的前提下,下面的代碼運行正常
public class TestOOM {
public static void main(String[] args) {
testStrongReference();
}
private static void testStrongReference() {
// 當 new byte為 1M 時,程序運行正常
byte[] buff = new byte[1024 * 1024 * 1];
}
}
但是如果我們將
byte[] buff = new byte[1024 * 1024 * 1];
替換為創(chuàng)建一個大小為 2M 的字節(jié)數(shù)組
byte[] buff = new byte[1024 * 1024 * 2];
則內(nèi)存不夠使用,程序直接報錯,強引用并不會被回收
接著來看一下軟引用會有什么不一樣,在下面的示例中連續(xù)創(chuàng)建了 10 個大小為 1M 的字節(jié)數(shù)組,并賦值給了軟引用,然后循環(huán)遍歷將這些對象打印出來。
public class TestOOM {
private static ListObject list = new ArrayList();
public static void main(String[] args) {
testSoftReference();
}
private static void testSoftReference() {
for (int i = 0; i 10; i++) {
byte[] buff = new byte[1024 * 1024];
SoftReferencebyte[] sr = new SoftReference(buff);
list.add(sr);
}
System.gc(); //主動通知垃圾回收
for(int i=0; i list.size(); i++){
Object obj = ((SoftReference) list.get(i)).get();
System.out.println(obj);
}
}
}
打印結(jié)果:
我們發(fā)現(xiàn)無論循環(huán)創(chuàng)建多少個軟引用對象,打印結(jié)果總是只有最后一個對象被保留,其他的obj全都被置空回收了。
這里就說明了在內(nèi)存不足的情況下,軟引用將會被自動回收。
值得注意的一點 , 即使有 byte[] buff 引用指向?qū)ο? 且 buff 是一個strong reference, 但是 SoftReference sr 指向的對象仍然被回收了,這是因為Java的編譯器發(fā)現(xiàn)了在之后的代碼中, buff 已經(jīng)沒有被使用了, 所以自動進行了優(yōu)化。
如果我們將上面示例稍微修改一下:
private static void testSoftReference() {
byte[] buff = null;
for (int i = 0; i 10; i++) {
buff = new byte[1024 * 1024];
SoftReferencebyte[] sr = new SoftReference(buff);
list.add(sr);
}
System.gc(); //主動通知垃圾回收
for(int i=0; i list.size(); i++){
Object obj = ((SoftReference) list.get(i)).get();
System.out.println(obj);
}
System.out.println("buff: " + buff.toString());
}
則 buff 會因為強引用的存在,而無法被垃圾回收,從而拋出OOM的錯誤。
如果一個對象惟一剩下的引用是軟引用,那么該對象是軟可及的(softly reachable)。垃圾收集器并不像其收集弱可及的對象一樣盡量地收集軟可及的對象,相反,它只在真正 “需要” 內(nèi)存時才收集軟可及的對象。
三,弱引用
弱引用的引用強度比軟引用要更弱一些,無論內(nèi)存是否足夠,只要 JVM 開始進行垃圾回收,那些被弱引用關聯(lián)的對象都會被回收。在 JDK1.2 之后,用 java.lang.ref.WeakReference 來表示弱引用。
我們以與軟引用同樣的方式來測試一下弱引用:
private static void testWeakReference() {
for (int i = 0; i 10; i++) {
byte[] buff = new byte[1024 * 1024];
WeakReferencebyte[] sr = new WeakReference(buff);
list.add(sr);
}
System.gc(); //主動通知垃圾回收
for(int i=0; i list.size(); i++){
Object obj = ((WeakReference) list.get(i)).get();
System.out.println(obj);
}
}
打印結(jié)果:
可以發(fā)現(xiàn)所有被弱引用關聯(lián)的對象都被垃圾回收了。
四,虛引用
虛引用是最弱的一種引用關系,如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,它隨時可能會被回收,在 JDK1.2 之后,用 PhantomReference 類來表示,通過查看這個類的源碼,發(fā)現(xiàn)它只有一個構(gòu)造函數(shù)和一個 get() 方法,而且它的 get() 方法僅僅是返回一個null,也就是說將永遠無法通過虛引用來獲取對象,虛引用必須要和 ReferenceQueue 引用隊列一起使用。
public class PhantomReferenceT extends ReferenceT {
/**
* Returns this reference object's referent. Because the referent of a
* phantom reference is always inaccessible, this method always returns
* codenull/code.
*
* @return codenull/code
*/
public T get() {
return null;
}
public PhantomReference(T referent, ReferenceQueue? super T q) {
super(referent, q);
}
}
那么傳入它的構(gòu)造方法中的 ReferenceQueue 又是如何使用的呢?
五,引用隊列(ReferenceQueue)
引用隊列可以與軟引用、弱引用以及虛引用一起配合使用,當垃圾回收器準備回收一個對象時,如果發(fā)現(xiàn)它還有引用,那么就會在回收對象之前,把這個引用加入到與之關聯(lián)的引用隊列中去。程序可以通過判斷引用隊列中是否已經(jīng)加入了引用,來判斷被引用的對象是否將要被垃圾回收,這樣就可以在對象被回收之前采取一些必要的措施。
個人理解:深入理解JAVA虛擬機一書中有這樣一句描述:“為一個對象設置虛引用關聯(lián)的唯一目的就是能在這個對象被收集器回收時收到一個系統(tǒng)通知”。所以虛引用更多的是用于對象回收的監(jiān)聽,能做的功能如下:
重要對象回收監(jiān)聽 進行日志統(tǒng)計
系統(tǒng)gc監(jiān)聽 因為虛引用每次GC都會被回收,那么我們就可以通過虛引用來判斷gc的頻率,如果頻率過大,內(nèi)存使用可能存在問題,才導致了系統(tǒng)gc頻繁調(diào)用
參考Java幽靈引用的作用
強引用:
只要引用存在,垃圾回收器永遠不會回收
Object obj = new Object();
//可直接通過obj取得對應的對象 如obj.equels(new Object());
而這樣 obj對象對后面new Object的一個強引用,只有當obj這個引用被釋放之后,對象才會被釋放掉,這也是我們經(jīng)常所用到的編碼形式。
軟引用:
非必須引用,內(nèi)存溢出之前進行回收,可以通過以下代碼實現(xiàn)
Object obj = new Object();
SoftReferenceObject sf = new SoftReferenceObject(obj);
obj = null;
sf.get();//有時候會返回null
這時候sf是對obj的一個軟引用,通過sf.get()方法可以取到這個對象,當然,當這個對象被標記為需要回收的對象時,則返回null;
軟引用主要用戶實現(xiàn)類似緩存的功能,在內(nèi)存足夠的情況下直接通過軟引用取值,無需從繁忙的真實來源查詢數(shù)據(jù),提升速度;當內(nèi)存不足時,自動刪除這部分緩存數(shù)據(jù),從真正的來源查詢這些數(shù)據(jù)。
弱引用:
第二次垃圾回收時回收,可以通過如下代碼實現(xiàn)
Object obj = new Object();
WeakReferenceObject wf = new WeakReferenceObject(obj);
obj = null;
wf.get();//有時候會返回null
wf.isEnQueued();//返回是否被垃圾回收器標記為即將回收的垃圾
弱引用是在第二次垃圾回收時回收,短時間內(nèi)通過弱引用取對應的數(shù)據(jù),可以取到,當執(zhí)行過第二次垃圾回收時,將返回null。
弱引用主要用于監(jiān)控對象是否已經(jīng)被垃圾回收器標記為即將回收的垃圾,可以通過弱引用的isEnQueued方法返回對象是否被垃圾回收器標記。
虛引用:
垃圾回收時回收,無法通過引用取到對象值,可以通過如下代碼實現(xiàn)
Object obj = new Object();
PhantomReferenceObject pf = new PhantomReferenceObject(obj);
obj=null;
pf.get();//永遠返回null
pf.isEnQueued();//返回是否從內(nèi)存中已經(jīng)刪除
虛引用是每次垃圾回收的時候都會被回收,通過虛引用的get方法永遠獲取到的數(shù)據(jù)為null,因此也被成為幽靈引用。
虛引用主要用于檢測對象是否已經(jīng)從內(nèi)存中刪除。
Java中弱引用VS軟引用
Java中有如下四種類型的引用:
1.強引用(Strong Reference)
2.弱引用(WeakReference)
3.軟引用(SoftReference)
4.虛引用(PhantomReference)
強引用是我們在編程過程中使用的最簡單的引用,如代碼String s=”abc”中變量s就是字符串對象”abc”的一個強引用。任何被強引用指向的對象都不能被垃圾回收器回收,這些對象都是在程序中需要的。弱引用使用java.lang.ref.WeakReference class 類來表示,你可以使用如下代碼創(chuàng)建弱引用:
復制代碼代碼如下:
Counter counter = new Counter(); // strong reference - line 1
WeakReferenceCounter weakCounter = new WeakReferenceCounter(counter); //weak reference
counter = null; // now Counter object is eligible for garbage collection
現(xiàn)在只要你給強引用對象counter賦空值null,該對象就可以被垃圾回收器回收。因為該對象此時不再含有其他強引用,即使指向該對象的弱引用weakCounter也無法阻止垃圾回收器對該對象的回收。相反的,如果該對象含有軟引用,Counter對象不會立即被回收,除非JVM需要內(nèi)存。Java中的軟引用使用java.lang.ref.SoftReference類來表示,你可以使用如下代碼創(chuàng)建軟引用:
復制代碼代碼如下:
Counter prime = new Counter(); // prime holds a strong reference – line 2
SoftReference soft = new SoftReference(prime) ; //soft reference variable has SoftReference to Counter Object created at line 2
prime = null; // now Counter object is eligible for garbage collection but only be collected when JVM absolutely needs memory
強引用置空之后,代碼的第二行為對象Counter創(chuàng)建了一個軟引用,該引用同樣不能阻止垃圾回收器回收對象,但是可以延遲回收,與弱引用中急切回收對象不同。鑒于軟引用和弱引用的這一區(qū)別,軟引用更適用于緩存機制,而弱引用更適用于存貯元數(shù)據(jù)。另一個使用弱引用的例子是WeakHashMap,它是除HashMap和TreeMap之外,Map接口的另一種實現(xiàn)。WeakHashMap有一個特點:map中的鍵值(keys)都被封裝成弱引用,也就是說一旦強引用被刪除,WeakHashMap內(nèi)部的弱引用就無法阻止該對象被垃圾回收器回收。
虛引用是java.lang.ref package包中第三種可用的引用,使用java.lang.ref.PhantomReference類來表示。擁有虛引用的對象可以在任何時候被垃圾回收器回收。和弱引用和軟引用相似,你可以通過如下代碼創(chuàng)建虛引用:
復制代碼代碼如下:
DigitalCounter digit = new DigitalCounter(); // digit reference variable has strong reference – line 3
PhantomReference phantom = new PhantomReference(digit); // phantom reference to object created at line 3
digit = null;
一旦移除強引用,第三行的DigitalCounter對象可以在任何時候被垃圾回收器回收。因為只有一個虛引用指向該對象,而虛引用無法阻止垃圾回收器回收對象。
除了了解弱引用、軟引用、虛引用和WeakHashMap,還需要了解ReferenceQueue。在創(chuàng)建任何弱引用、軟引用和虛引用的過程中你可以通過如下代碼提供引用隊列ReferenceQueue:
復制代碼代碼如下:
ReferenceQueue refQueue = new ReferenceQueue(); //reference will be stored in this queue for cleanup
DigitalCounter digit = new DigitalCounter();
PhantomReferenceDigitalCounter phantom = new PhantomReferenceDigitalCounter(digit, refQueue);
引用實例被添加在引用隊列中,你可以再任何時候通過查詢引用隊列回收對象。一個對象的生命周期可以通過下圖進行描述:
這就是Java中弱引用和軟引用的區(qū)別。我們還學到了一些基本的引用類:弱引用、軟引用、虛引用以及WeakHashMap和WeakHashMap??傊侠淼氖褂靡每梢詭椭厥掌鞲玫墓芾鞪ava內(nèi)存。