Java引用類型

Java引用類型

爲了更靈活的控制對象的生命週期,引用被劃分爲強引用、軟引用、弱引用、虛引用四種類型,每種類型有不一樣的生命週期,它們不一樣的地方就在於垃圾回收器對待它們會使用不一樣的處理方式。緩存

強引用

定義

強引用是使用最廣泛的引用。若是一個對象具備強引用,那垃圾回收器寧願拋出OOM(OutOfMemoryError)也不會回收它。

例如:this

Object o=new Object();   //  強引用
強可達

若是一個對象與GC Roots之間存在強引用,則稱這個對象爲強可達(strong reachable)對象。

特色

在JVM進行GC的時候,只要對象有強引用與其關聯,就絕對不會對它進行回收,即便已經內存不足了也不會收回有強引用指向的對象。線程

若是你不須要使用某個對象了,能夠將相應的引用設置爲null,消除強引用來幫助垃圾回收器進行回收。設計

若是在一個方法的內部有一個變量s持有一個對象(Object)的強引用,那麼這個變量s保存在棧中,而真正的引用內容(object)保存在堆中。當這個方法運行完成後就會退出方法棧,則引用s也會被銷燬,這個object就會被回收。可是當這個s是全局變量時,就須要在再也不使用這個對象時賦值爲null,由於有強引用關聯的對象是不會被垃圾回收的。code

A a = new A();
B b = new B(a);
a = null;

這裏a和b是強引用,當把 a = null 時,這時 a 再也不指向 A 的地址。講道理:當某個對象再也不與其餘引用關聯時,就會被 垃圾回收器斷定爲可回收,在GC中就會被回收掉。可是這裏a = null 時,A 對象不能被回收,由於還有一個B對象持有其強引用,這時候就形成了內存泄漏對象

另外一個內存泄露的例子:生命週期

public static ArrayList<Object> list = new ArrayList<Object>();
public void stackOverflowTest(Object object){
    list.add(object);
    object = null;
}

小結

  • 強引用就是最普通的引用
  • 可使用強引用直接訪問目標對象
  • 強引用指向的對象在任什麼時候候都不會被系統回收
  • 強引用可能會致使內存泄漏
  • 過多的強引用會致使OOM

軟引用

定義

軟引用是使用SoftReference建立的引用,強度弱於強引用,被其引用的對象在內存不足的時候會被回收,不會產生內存溢出。
String str=new String("abc");                                    // 強引用
SoftReference<String> softRef=new SoftReference<String>(str);    // 軟引用

str = null;     // 消除強引用,如今只剩下軟引用與其關聯,該String對象爲軟可達狀態
str = softRef.get();  // 從新關聯上強引用
軟可達

若是一個對象與GC Roots之間不存在強引用,可是存在軟引用,則稱這個對象爲 軟可達(soft reachable)對象。

特色

若是一個對象與GC Roots之間存在軟引用關聯時,那麼垃圾回收器對它的態度就取決於內存的緊張程度了。若是內存空間足夠,垃圾回收器就不會回收這個對象,但若是內存空間不足了,它就難逃被回收的厄運。隊列

在垃圾回收器沒有回收它的時候,軟可達對象就像強可達對象同樣,能夠被程序正常訪問和使用,可是須要經過軟引用對象間接訪問,須要的話也能從新使用強引用將其關聯。因此軟引用適合用來作內存敏感的高速緩存。內存

在垃圾回收器回收一個對象前,SoftReference類所提供的get方法會返回Java對象的強引用,一旦垃圾線程回收該對象以後,get方法將返回null。因此在獲取軟引用對象的代碼中,必定要先判斷返回是否爲null,以避免出現NullPointerException異常而致使應用崩潰。資源

注意,SoftReference對象是用來保存軟引用的,但它同時也是一個Java對象。因此,當軟可及對象被回收以後,雖然這個SoftReference對象的get()方法返回null,但SoftReference對象自己並非null,而此時這個SoftReference對象已經再也不具備存在的價值,須要一個適當的清除機制,避免大量SoftReference對象帶來的內存泄漏。

ReferenceQueue就是用來保存這些須要被清理的引用對象的。軟引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。

弱引用

定義

弱引用是使用WeakReference建立的引用,弱引用也是用來描述非必需對象的,它是比軟引用更弱的引用類型。在發生GC時,只要發現弱引用,無論系統堆空間是否足夠,都會將對象進行回收。

String str=new String("abc");    
WeakReference<String> abcWeakRef = new WeakReference<String>(str);
str=null;
弱可達

若是一個對象與GC Roots之間僅存在弱引用,則稱這個對象爲弱可達(weakly reachable)對象。

特色

若是這個對象是偶爾的使用,而且但願在使用時隨時就能獲取到,但又不想影響此對象的垃圾收集,那麼你應該用 Weak Reference 來記住此對象。

下面的代碼會讓str再次變爲一個強引用:

String  abc = abcWeakRef.get();

弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。

通常來講,不多直接使用WeakReference,而是使用WeakHashMap。在WeakHashMap中,內部有一個引用隊列,插入的元素會被包裹成WeakReference,並加入隊列中,用來作緩存再合適不過。

在Tomcat的緩存中,其實就用到了WeakHashMap:

public final class ConcurrentCache<K,V> {
    private final int size;
    private final Map<K,V> eden;
    private final Map<K,V> longterm;

    public ConcurrentCache(int size) {
        this.size = size;
        this.eden = new ConcurrentHashMap<>(size);
        this.longterm = new WeakHashMap<>(size);
    }

    public V get(K k) {
        // 先從eden中取
        V v = this.eden.get(k);
        if (v == null) {
            // 若是取不到再從longterm中取
            synchronized (longterm) {
                v = this.longterm.get(k);
            }
            // 若是取到則從新放到eden中
            if (v != null) {
                this.eden.put(k, v);
            }
        }
        return v;
    }

    public void put(K k, V v) {
        if (this.eden.size() >= size) {
            // 若是eden中的元素數量大於指定容量,將全部元素放到longterm中
            synchronized (longterm) {
                this.longterm.putAll(this.eden);
            }
            this.eden.clear();
        }
        this.eden.put(k, v);
    }
}

通過這樣的設計,相對經常使用的對象都能在eden緩存中找到,不經常使用(有可能被銷燬的對象)的則進入longterm緩存。而longterm的key的實際對象沒有其餘引用指向它時,gc就會自動回收heap中該弱引用指向的實際對象,並將弱引用放入其引用隊列中。

弱引用與軟引用對比

弱引用與軟引用的區別在於:

  • 只具備弱引用的對象擁有更短暫的生命週期。
  • 被垃圾回收器回收的時機不同,在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。而被軟引用關聯的對象只有在內存不足時纔會被回收。
  • 弱引用不會影響GC,而軟引用會必定程度上對GC形成影響。

類似之處:都是用來描述非必需對象的。

那麼何時用SoftReference,何時用WeakReference呢?
若是緩存的對象是比較大的對象,使用頻率相對較高的對象,那麼使用SoftReference會更好,由於這樣能讓緩存對象有更長的生命週期。

若是緩存對象都是比較小的對象,使用頻率通常或者相對較低,那麼使用WeakReference會更合適。

固然,若是實在不知道選哪一個,通常而言,用做緩存時使用WeakHashMap都不會有太大問題。

虛引用

定義

虛引用是使用PhantomReference建立的引用,虛引用也稱爲幽靈引用或者幻影引用,是全部引用類型中最弱的一個。一個對象是否有虛引用的存在,徹底不會對其生命週期構成影響,也沒法經過虛引用得到一個對象實例。

虛可達

若是一個對象與GC Roots之間僅存在虛引用,則稱這個對象爲虛可達(phantom reachable)對象。

特色

虛引用做用在於跟蹤垃圾回收過程,在對象被收集器回收時收到一個系統通知。 當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在垃圾回收後,將這個虛引用加入引用隊列,在其關聯的虛引用出隊前,不會完全銷燬該對象。 因此能夠經過檢查引用隊列中是否有相應的虛引用來判斷對象是否已經被回收了。

若是一個對象沒有強引用和軟引用,對於垃圾回收器而言即是能夠被清除的,在清除以前,會調用其finalize方法,若是一個對象已經被調用過finalize方法可是尚未被釋放,它就變成了一個虛可達對象。

使用虛引用的目的就是爲了得知對象被GC的時機,因此能夠利用虛引用來進行銷燬前的一些操做,好比說資源釋放等。這個虛引用對於對象而言徹底是無感知的,有沒有徹底同樣,可是對於虛引用的使用者而言,就像是待觀察的對象的把脈線,能夠經過它來觀察對象是否已經被回收,從而進行相應的處理。

事實上,虛引用有一個很重要的用途就是用來作堆外內存的釋放,DirectByteBuffer就是經過虛引用來實現堆外內存的釋放的。

小結

  • 虛引用是最弱的引用
  • 虛引用對對象而言是無感知的,對象有虛引用跟沒有是徹底同樣的
  • 虛引用不會影響對象的生命週期
  • 虛引用能夠用來作爲對象是否存活的監控

總結

引用級別:強引用 > 軟引用 > 弱引用 > 虛引用

引用類型 引用對象被垃圾回收的時間 用途 是否能夠轉爲強引用 對應的類
強引用 歷來不會 通常用途,保持對象不被回收 能夠 默認
軟引用 內存不足時 緩存,保持對象在內存足夠時不被回收 能夠 SoftReference
弱引用 垃圾回收時 緩存,僅僅在對象仍被使用時保持其不被回收 能夠 WeakReference WeakHashMap
虛引用 進行垃圾回收時 跟蹤GC過程,在對象被回收前進行一些清理工 能夠 PhantomReference
相關文章
相關標籤/搜索