【Java】Java中的四種對象引用


從JDK1.2開始,Java中的引用類型分為四種,分別是:

1.強引用(StrongReference)
  這種引用是平時開發中最常用的,例如 String strong = new String("Strong Reference")當一個實例對象具有強引用時,垃圾回收器不會回收該對象,當內存不足時,寧願拋出OutOfMemeryError異常也不會通過回收強引用的對象,因為JVM認為強引用的對象是用戶正在使用的對象,它無法分辨出到底該回收哪個,強行回收有可能導致系統嚴重錯誤。

2.軟引用(SoftRefernce)
  如果一個對象只有軟引用, 那么只有當內存不足時,JVM才會去回收該對象,其他情況不會回收。軟引用可以結合ReferenceQueue來使用,當由於系統內存不足,導致軟引用的對象被回收了,JVM會把這個軟引用加入到與之相關聯的ReferenceQueue中。
ReferenceQueue referenceQueue = new ReferenceQueue();
SoftReference<Book> softReference = new SoftReference<>(new Book(), referenceQueue);
Book book = softReference.get();
Reference reference = referenceQueue.poll();

當系統內存不足時,觸發gc,這個Book就會被回收,reference 將不為null。


3.弱引用(WeakReference)
   只有弱引用的對象,當JVM觸發gc時,就會回收該對象。與軟引用不同的是,不管是否內存不足,弱引用都會被回收。弱引用可以結合ReferenceQueue來使用,當由於系統觸發gc,導致軟引用的對象被回收了,JVM會把這個弱引用加入到與之相關聯的ReferenceQueue中,不過由於垃圾收集器線程的優先級很低,所以弱引用不一定會被很快回收。下面通過一個主動觸發gc的例子來驗證此結論。

ReferenceQueue referenceQueue = new ReferenceQueue();
WeakReference<Book> weakReference = new WeakReference(new Book(), referenceQueue);
Book book = softReference.get();
System.gc();
//Runtime.getRuntime().gc();
Reference reference = referenceQueue.poll();

當然這不是每次都能復現,因為我們調用System.gc()只是告訴JVM該回收垃圾了,但是它什么時候做還是不一定的,但就我測試來看,只要多寫幾次System.gc(),復現的概率還是很高的。

  PS,

  ThreadLocalMap中的靜態內部類Entry類就是繼承了WeakRefence<ThreadLocal>這個類在構造方法中

static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
               //調用父類WeakReference的構造方法
                super(k);
                value = v;
            }
        }

  

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
           //ThreadLocalMap將ThreadLocal作為key,(注意這個Key有點特殊,這個Key被弱引用關聯)
            map.set(this, value);
        else
            createMap(t, value);
    }
 private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                //e最終獲取的是其實是被弱引用WeakRerference關聯的ThreadLocal
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
//Reference中的Get方法    
    public T get() {
        return this.referent;
    }

注意:

  由於Thread 線程對象都具有成員變量ThreadLocal.ThreadLocalMap,

  而ThreadLocalMap中的key就是當前ThreadLocal對象,而value就是我們想要和線程上下文綁定的數據,

  同時ThreadLocal又被弱引用關聯,

  因此在線程生命周期結束時,其成員變量ThreadLocal.ThreadLocalMap,也會為null,此時內部的Entry會做額外的操作將value置為null,

       這樣就滿足了弱引用被回收的條件:當且僅當Entry的key持有弱引用時,該ThreadLocal就會被回收,解決了ThreadLocal對象內存泄漏的問題;

 

 

4.虛引用(PhantomReference)
  如果一個對象只有虛引用在引用它,垃圾回收器是可以在任意時候對其進行回收的,虛引用主要用來跟蹤對象被垃圾回收器回收的活動,當被回收時,JVM會把這個弱引用加入到與之相關聯的ReferenceQueue中。與軟引用和弱引用不同的是,虛引用必須有一個與之關聯的ReferenceQueue,通過phantomReference.get()得到的值為null,試想一下,如果沒有ReferenceQueue與之關聯還有什么存在的價值呢?
 
PhantomReference<Book> phantomReference = new PhantomReference<>(new Book(), referenceQueue);
Book book = phantomReference.get(); //此值為null
Reference reference = referenceQueue.poll();

 



 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM