Java中的 WeakReference 和 SoftReference


我們知道Java語言中沒有指針,取而代之的是引用reference。Java中的引用又可以分為四種:強引用,弱引用(WeakReference),軟引用(SoftReference),虛引用(PhantomReference)。其中強引用,就是我們平時使用的最多的最普通的引用,虛引用一般我們是沒有機會使用到的。所以我們主要了解下 WeakReference 和 SoftReference(除了上面說的四種引用之外,其實還有一種引用——原子引用AtomicReference,用於並發編程環境)。

1. 先上一段代碼:

public class ReferenceTest {
    public static void main(String[] args){
        LinkedList<byte[]> list = new LinkedList<>();
        for(int i=0; i<1024; i++){
            list.add(new byte[1024*1024]);
        }
    }
}

上面的代碼會拋出:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

堆內存溢出。因為我們不斷的在堆上分配一個 1M 大小的 byte[]對象,並且將該引用加入到 list 中,循環1024次,需要占用 1G 的堆內存,從而導致 heap space OutOfMemory.

2. 我們使用 WeekReference 對代碼進行修改:

public class ReferenceTest {
    public static void main(String[] args) {
        long beginTime = System.nanoTime();
        LinkedList<WeakReference<byte[]>> list = new LinkedList<>();
        for (int i = 0; i < 1024; i++) {
            list.add(new WeakReference<>(new byte[1024 * 1024]));
        }
        long endTime = System.nanoTime();
        System.out.println(endTime - beginTime);
    }
}

輸出的結果:195947704 (0.19秒)

我們發現堆內存溢出的錯誤沒有了。這是什么原因呢。因為我們使用了 弱引用WeekReference 來引用堆上的 1M 的byte[]對象,而弱引用WeekReference引用的對象,如果僅僅只被弱引用,而沒有被強引用的話,在下一次GC時,就會回收該對象占用的內存,所以不會內存溢出。

3. 我們使用 SoftReference 對代碼進行修改:

public class ReferenceTest {
    public static void main(String[] args) {
        long beginTime = System.nanoTime();
        LinkedList<SoftReference<byte[]>> list = new LinkedList<>();
        for (int i = 0; i < 1024; i++) {
            list.add(new SoftReference<>(new byte[1024 * 1024]));
        }
        long endTime = System.nanoTime();
        System.out.println(endTime - beginTime);
    }
}

輸出結果:1499904286 (1.5秒)

我們發現堆內存溢出的錯誤也沒有了。因為我們使用了 軟引用SoftReference 來引用堆上的 1M 的byte[]對象,而軟引用SoftReference引用的對象,如果僅僅只被軟引用,而沒有被強引用的話,在內存空間不足時,GC 就會回收該對象占用的內存,所以不會內存溢出。

但是我們注意到 采用WeekReference和采用SoftReference所花費的時間,有接近10被的差距。原因應該是,SoftReference只有在內存空間不足時,GC才會回收對象占用的空間,而這時進行的是 full GC,full GC會導致 STW 程序暫停,所以花費的時間過多。

4. 總結

強引用:只要堆上的對象,被至少一個強引用所指向,那么GC就不會回收該對象的內存空間。

弱引用:只要堆上的對象僅僅只被弱引用所指向,不管當前內存空間是否足夠,下次GC都會回收對象的內存空間。

軟引用:只要堆上的對象僅僅只被軟引用所指向,並且當內存空間不足時,GC才會回收對象的內存空間。

WeakReference 和 SoftReference一般使用在構造一個緩存系統,比如使用一個map來構造。因為緩存系統是一個“全生命期”的對象,系統停止,緩存對象才會被銷毀,所以當我們不斷的想緩存對象中添加對象時,那么就會導致該緩存對象map所引用的對象越來越多,而因為是強引用,這些被放進map緩存了的對象不能被GC所回收,那么就導致系統堆內存占用會越來越大,從而最終導致內存溢出。

那么此時我們就可以使用 WeakReference 或 SoftReference了,將強引用通過WeakReference 和 SoftReference 包裝之后,變成弱引用和軟引用,那么當緩存中的對象,僅僅被緩存map所引用時,那么分別在下次GC和內存不足GC時就會回收這些對象占用的內存。其實JDK給我們提供了一個專門的類:WeakHashMap ,弱引用的hashMap,所以構造緩存系統是,我們可以考慮使用它。

其實這里引出了另外一個問題,jdk中眾多的map,我們應該如何進行選擇

HashMap

ConcurrentHashMap

TreeMap

WeakHashMap

LinkedHashMap

Collections.synchronizedMap

Hashtable

等等。我們在選擇一個map時,應該好好的考慮下,那個更加適合我們的需求。

 


免責聲明!

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



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