【JVM】如何理解強引用、軟引用、弱引用、虛引用?

總體架構

強引用

強引用是默認支持,當內存不足的時候,JVM開始垃圾回收,對於強引用的對象,就算是出現了OOM也不會回收對象。java

強引用是最多見的普通對象引用,只要還有強引用指向對象,對象就存活,垃圾回收器不會處理存活對象。通常把一個對象賦給一個引用變量,這個引用變量就是強引用。當一個對象被強引用變量所引用,它就處於可達狀態,是不會被垃圾回收的,即便以後都不會再用到了,也不會回收。所以強引用是形成Java內存泄漏的主要緣由之一緩存

關於Java內存泄漏的詳細內容,能夠參考這篇博客:http://www.javashuo.com/article/p-ppqsozta-dp.html架構

對於一個普通對象,若是沒有其餘引用關係,只要超過了引用的做用域或者顯式地將相應的強引用賦值爲null,通常認爲就是能夠被垃圾回收了。(具體的回收時機看垃圾回收策略)性能

下例中,b就是強引用。spa

1     public static void main(String[] args) {
2         Object a = new Object();
3         Object b = a;
4         a = null;
5         System.out.println(b);//java.lang.Object@4554617c
6     }

軟引用

軟引用是一種相對強引用弱化了一些的引用,用java.lang.ref.SoftReference實現,可讓對象豁免一些垃圾收集。當系統內存充足的時候,不會被回收;當系統內存不足的時候,會被回收。.net

軟引用通常用於對內存敏感的程序中,好比高速緩存。code

 1 import java.lang.ref.SoftReference;
 2 
 3 public class SoftReferenceDemo {
 4     public static void main(String[] args) {
 5         Object a = new Object();
 6         SoftReference<Object> softReference = new SoftReference<>(a);//軟引用
 7         //a和軟引用指向同一個對象
 8         System.out.println(a);//java.lang.Object@4554617c
 9         System.out.println(softReference.get());//java.lang.Object@4554617c
10 
11         //內存夠用,軟引用不會被回收
12         a = null;
13         System.gc();//內存夠用不會自動gc,手動喚醒gc
14         System.out.println(a);//null
15         System.out.println(softReference.get());//java.lang.Object@4554617c
16 
17         //內存不夠用時
18         try{
19             //配置Xms和Xmx爲5MB
20             byte[] bytes = new byte[1024*1024*30];//設置30MB超內存
21         }catch (Throwable e){
22             e.printStackTrace();
23         }finally {
24             System.out.println(a);//null
25             System.out.println(softReference.get());//null
26         }
27     }
28 }

使用場景

一個應用須要讀取大量的本地圖片,若是每次讀取都從硬盤讀取會嚴重影響性能,若是一次性所有加載到內存,內存可能會溢出。對象

可使用軟引用解決這個問題,使用一個HashMap來保存圖片路徑和圖片對象管理的軟引用之間的映射關係,內存不足時,JVM會自動回收緩存圖片對象的佔用空間,有效地避免了OOM(Out Of Memory)問題。blog

Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>生命週期

弱引用

弱引用須要用java.lang.ref.WeakReference實現,它比軟引用的生存期更短,對於弱引用的對象來講,只要垃圾回收機制一運行,無論JVM的內存空間是否夠,都會回收該對象的佔用內存。

 1 import java.lang.ref.WeakReference;
 2 
 3 public class SoftReferenceDemo {
 4     public static void main(String[] args) {
 5         Object a = new Object();
 6         WeakReference<Object> softReference = new WeakReference<>(a);//軟引用
 7         //a和弱引用指向同一個對象
 8         System.out.println(a);//java.lang.Object@4554617c
 9         System.out.println(softReference.get());//java.lang.Object@4554617c
10 
11         //內存夠用,弱引用也會被回收
12         a = null;
13         System.gc();//內存夠用不會自動gc,手動喚醒gc
14         System.out.println(a);//null
15         System.out.println(softReference.get());//null
16     }
17 }

關於WeakHashMap

 1     public static void weakHashMapTest() {
 2         Integer key = new Integer(1);
 3         String value = "李四";
 4         Map<Integer,String> weakHashMap = new WeakHashMap();
 5         weakHashMap.put(key, value);
 6         System.out.println(weakHashMap);//{1=李四}
 7         key = null;
 8         System.gc();
 9         System.out.println(weakHashMap);//{}
10     }
11 
12     public static void hashMapTest() {
13         HashMap<Integer,String> map = new HashMap<>();
14         Integer key = 1;
15         String value = "張三";
16         map.put(key,value);
17         System.out.println(map);//{1=張三}
18         key = null;
19         System.gc();
20         System.out.println(map);//{1=張三}
21     }

在HashMap中,鍵被置爲null,喚醒gc後,不會垃圾回收鍵爲null的鍵值對。可是在WeakHashMap中,鍵被置爲null,喚醒gc後,鍵爲null的鍵值對會被回收。

虛引用

虛引用要經過java.lang.ref.PhantomReference類來實現,虛引用不會決定對象的生命週期,若是一個對象只有虛引用,就至關於沒有引用,在任什麼時候候均可能會被垃圾回收器回收。它不能單獨使用也不能訪問對象,虛引用必須和引用隊列聯合使用

虛引用的主要做用是跟蹤對象被垃圾回收的狀態,僅僅是提供一種確保對象被finalize之後,作某些事情的機制。

PhantomReference的get方法老是返回null,所以沒法訪問對應的引用對象,設置虛引用關聯惟一的目的是在對象被收集器回收的時候收到一個系統通知,或者後續添加進一步的處理。Java容許使用finalize()方法在垃圾回收器將對象從內存中清理出去以前作一些必要的清理工做。【例如實現一個監控對象的通知機制】

引用隊列

WeakReference和ReferenceQueue的聯合使用效果:

 1     public static void weakReferenceTest() {
 2         Object a = new Object();
 3         ReferenceQueue<Object> queue = new ReferenceQueue<>();
 4         WeakReference<Object> weakReference = new WeakReference<>(a,queue);
 5         System.out.println(a);//java.lang.Object@4554617c
 6         System.out.println(weakReference.get());//java.lang.Object@4554617c
 7         System.out.println(queue.poll());//null
 8         System.out.println("-------------------");
 9         a = null;
10         System.gc();
11         System.out.println(a);//null
12         System.out.println(weakReference.get());//null
13         //虛引用在回收以前被加入到了引用隊列中
14         System.out.println(queue.poll());//java.lang.ref.WeakReference@74a14482
15     }

PhantomReference和ReferenceQueue的聯合使用效果:

 1     public static void phantomReferenceTest() {
 2         Object a = new Object();
 3         ReferenceQueue<Object> queue = new ReferenceQueue<>();
 4         PhantomReference<Object> phantomReference = new PhantomReference<>(a,queue);
 5         System.out.println(a);//java.lang.Object@4554617c
 6         System.out.println(phantomReference.get());//null
 7         System.out.println(queue.poll());//null
 8         System.out.println("-------------------");
 9         a = null;
10         System.gc();
11         System.out.println(a);//null
12         System.out.println(phantomReference.get());//null
13         //引用在回收以前被加入到了引用隊列中
14         System.out.println(queue.poll());//java.lang.ref.WeakReference@74a14482
15     }

總結

強引用:不回收。

軟引用:內存不夠就回收。

弱引用:必定回收。

虛引用:必定回收,get出來就是null,引用形同虛設,主要和引用隊列聯合使用,在finalize以前會被放到引用隊列中。

與根對象沒有引用關係的:引用不可達,必定回收。

相關文章
相關標籤/搜索