整體架構
強引用
強引用是默認支持,當內存不足的時候,JVM開始垃圾回收,對於強引用的對象,就算是出現了OOM也不會回收對象。
強引用是最常見的普通對象引用,只要還有強引用指向對象,對象就存活,垃圾回收器不會處理存活對象。一般把一個對象賦給一個引用變量,這個引用變量就是強引用。當一個對象被強引用變量所引用,它就處於可達狀態,是不會被垃圾回收的,即使之后都不會再用到了,也不會回收。因此強引用是造成Java內存泄漏的主要原因之一。
關於Java內存泄漏的詳細內容,可以參考這篇博客:https://blog.csdn.net/m0_38110132/article/details/81986334。
對於一個普通對象,如果沒有其他引用關系,只要超過了引用的作用域或者顯式地將相應的強引用賦值為null,一般認為就是可以被垃圾回收了。(具體的回收時機看垃圾回收策略)
下例中,b就是強引用。
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實現,可以讓對象豁免一些垃圾收集。當系統內存充足的時候,不會被回收;當系統內存不足的時候,會被回收。
軟引用一般用於對內存敏感的程序中,比如高速緩存。
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)問題。
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之前會被放到引用隊列中。
與根對象沒有引用關系的:引用不可達,一定回收。