標籤:
我們知道Java語言中沒有指標,取而代之的是引用reference。Java中的引用又可以分為四種:強引用,弱引用(WeakReference),軟引用(SoftReference),虛引用(PhantomReference)。其中強引用,就是我們平時使用的最多的最普通的引用,虛引用一般我們是沒有機會使用到的。所以我們主要瞭解下 WeakReference 和 SoftReference。
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時,應該好好的考慮下,那個更加適合我們的需求。
Java中的 WeakReference 和 SoftReference