對象的強、軟、弱和虛引用

在JDK 1.2之前的版本中,若一個對象不被任何變量引用,那麼程序就沒法再使用這個對象。也就是說,只有對象處於可觸及(reachable)狀態,程序才能使用它。從JDK 1.2版本開始,把對象的引用分爲4種級別,從而使程序能更加靈活地控制對象的生命週期。這4種級別由高到低依次爲:強引用、軟引用、弱引用和虛引用。java

 

⑴強引用(StrongReference)
強引用是使用最廣泛的引用。若是一個對象具備強引用,那垃圾回收器毫不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具備強引用的對象來解決內存不足的問題。  ps:強引用其實也就是咱們平時A a = new A()這個意思。

⑵軟引用(SoftReference)
若是一個對象只具備軟引用,則內存空間足夠,垃圾回收器就不會回收它;若是內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就能夠被程序使用。軟引用可用來實現內存敏感的高速緩存(下文給出示例)。
軟引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。

⑶弱引用(WeakReference)
弱引用與軟引用的區別在於:只具備弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。不過,因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快發現那些只具備弱引用的對象。
弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。

⑷虛引用(PhantomReference)
「虛引用」顧名思義,就是形同虛設,與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期,使用Reference.get()方法獲得的永遠是null,而不像軟或者弱引用還可能獲得被引用對象obj。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。
虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在於:若是想讓虛引用乾點實事的話,必須和引用隊列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,把這個虛引用加入到與之 關聯的引用隊列中。算法

package com.jv.interview;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;

public class TestReference {
    public volatile static boolean isRun = true;

    @SuppressWarnings("static-access")
    public static void main(String[] args) throws Exception {
        String abc = new String("abc");
        System.out.println(abc.getClass() + "@" + abc.hashCode());
        final ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
        new Thread() {
            public void run() {
                while (isRun) {
                	//只有當垃圾回收被觸發後,垃圾回收機制纔會把虛引用放入隊列,而後經過反射拿到obj實例
                	//可是有一點不明白,既然已經被垃圾回收了,爲何還能拿到obj實例喃,應該是從內存清除了啊??????
                    Object obj = referenceQueue.poll();
                    if (obj != null) {
                        try {
                            Field rereferent = Reference.class
                                    .getDeclaredField("referent");
                            rereferent.setAccessible(true);
                            //經過反射的Field拿到對象
                            Object result = rereferent.get(obj);
                            System.out.println("gc will collect:"
                                    + result.getClass() + "@"
                                    + result.hashCode() + "\t"
                                    + (String) result);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }.start();
        
        //綁定虛引用和隊列
        PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,
                referenceQueue);
        abc = null;
        Thread.currentThread().sleep(3000);
        //觸發GC,但不是必須發生
        System.gc();
        Thread.currentThread().sleep(3000);
        isRun = false;
    }
}



程序能夠經過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。若是程序發現某個虛引用已經被加入到引用隊列,那麼就能夠在所引用的對象的內存被回收以前採起必要的行動。數據庫

 


使用軟引用構建敏感數據的緩存
1 爲何須要使用軟引用

首先,咱們看一個僱員信息查詢系統的實例。咱們將使用一個Java語言實現的僱員信息查詢系統查詢存儲在磁盤文件或者數據庫中的僱員人事檔案信息。做爲一個用戶,咱們徹底有可能須要回頭去查看幾分鐘甚至幾秒鐘前查看過的僱員檔案信息(一樣,咱們在瀏覽WEB頁面的時候也常常會使用「後退」按鈕)。這時咱們一般會有兩種程序實現方式:一種是把過去查看過的僱員信息保存在內存中,每個存儲了僱員檔案信息的Java對象的生命週期貫穿整個應用程序始終;另外一種是當用戶開始查看其餘僱員的檔案信息的時候,把存儲了當前所查看的僱員檔案信息的Java對象結束引用,使得垃圾收集線程能夠回收其所佔用的內存空間,當用戶再次須要瀏覽該僱員的檔案信息的時候,從新構建該僱員的信息。很顯然,第一種實現方法將形成大量的內存浪費,而第二種實現的缺陷在於即便垃圾收集線程尚未進行垃圾收集,包含僱員檔案信息的對象仍然無缺地保存在內存中,應用程序也要從新構建一個對象。咱們知道,訪問磁盤文件、訪問網絡資源、查詢數據庫等操做都是影響應用程序執行性能的重要因素,若是能從新獲取那些還沒有被回收的Java對象的引用,必將減小沒必要要的訪問,大大提升程序的運行速度。

2 若是使用軟引用
SoftReference的特色是它的一個實例保存對一個Java對象的軟引用,該軟引用的存在不妨礙垃圾收集線程對該Java對象的回收。也就是說,一旦SoftReference保存了對一個Java對象的軟引用後,在垃圾線程對這個Java對象回收前,SoftReference類所提供的get()方法返回Java對象的強引用。另外,一旦垃圾線程回收該Java對象以後,get()方法將返回null。
看下面代碼:緩存

MyObject aRef = new
MyObject();網絡

SoftReference aSoftRef=new SoftReference(aRef); 
此時,對於這個MyObject對象,有兩個引用路徑,一個是來自SoftReference對象的軟引用,一個來自變量aReference的強引用,因此這個MyObject對象是強可及對象。
隨即,咱們能夠結束aReference對這個MyObject實例的強引用:性能

aRef = null;.net

此後,這個MyObject對象成爲了軟可及對象。若是垃圾收集線程進行內存垃圾收集,並不會由於有一個SoftReference對該對象的引用而始終保留該對象。Java虛擬機的垃圾收集線程對軟可及對象和其餘通常Java對象進行了區別對待:軟可及對象的清理是由垃圾收集線程根據其特定算法按照內存需求決定的。也就是說,垃圾收集線程會在虛擬機拋出OutOfMemoryError以前回收軟可及對象,並且虛擬機會盡量優先回收長時間閒置不用的軟可及對象,對那些剛剛構建的或剛剛使用過的「新」軟可反對象會被虛擬機儘量保留。在回收這些對象以前,咱們能夠經過:
MyObject anotherRef=(MyObject)aSoftRef.get(); 
從新得到對該實例的強引用。而回收以後,調用get()方法就只能獲得null了。

3 使用ReferenceQueue清除失去了軟引用對象的SoftReference
做爲一個Java對象,SoftReference對象除了具備保存軟引用的特殊性以外,也具備Java對象的通常性。因此,當軟可及對象被回收以後,雖然這個SoftReference對象的get()方法返回null,但這個SoftReference對象已經再也不具備存在的價值,須要一個適當的清除機制,避免大量SoftReference對象帶來的內存泄漏。在java.lang.ref包裏還提供了ReferenceQueue。若是在建立SoftReference對象的時候,使用了一個ReferenceQueue對象做爲參數提供給SoftReference的構造方法,如:線程

ReferenceQueue queue = new
ReferenceQueue();code

SoftReference
ref=new
SoftReference(aMyObject, queue); 
那麼當這個SoftReference所軟引用的aMyOhject被垃圾收集器回收的同時,ref所強引用的SoftReference對象被列入ReferenceQueue。也就是說,ReferenceQueue中保存的對象是Reference對象,並且是已經失去了它所軟引用的對象的Reference對象。另外從ReferenceQueue這個名字也能夠看出,它是一個隊列,當咱們調用它的poll()方法的時候,若是這個隊列中不是空隊列,那麼將返回隊列前面的那個Reference對象。
在任什麼時候候,咱們均可以調用ReferenceQueue的poll()方法來檢查是否有它所關心的非強可及對象被回收。若是隊列爲空,將返回一個null,不然該方法返回隊列中前面的一個Reference對象。利用這個方法,咱們能夠檢查哪一個SoftReference所軟引用的對象已經被回收。因而咱們能夠把這些失去所軟引用的對象的SoftReference對象清除掉。經常使用的方式爲:對象

SoftReference ref = null;

while ((ref = (EmployeeRef) q.poll()) != null) {

// 清除ref

}

 

原文地址:http://blog.csdn.net/coding_or_coded/article/details/6603549

相關文章
相關標籤/搜索