面試官考察Java引用會問到強引用、弱引用、軟引用、虛引用,具體有什麼區別?本篇單獨來詳解 @mikechen Java引用 從JDK 1.2版本開始,對象的引用被劃分為4種級別,從而使程式能更加靈活地控制對象的生命周期,這4種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。 強引用 強引用是最 ...
面試官考察Java引用會問到強引用、弱引用、軟引用、虛引用,具體有什麼區別?本篇單獨來詳解 @mikechen
Java引用
從JDK 1.2版本開始,對象的引用被劃分為4種級別,從而使程式能更加靈活地控制對象的生命周期,這4種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。
強引用
強引用是最普遍的引用,一般把一個對象賦給一個引用變數,這個引用變數就是強引用。
比如:
// 強引用 MikeChen mikechen=new MikeChen();
在一個方法的內部有一個強引用,這個引用保存在Java棧中,而真正的引用內容(MikeChen)保存在Java堆中。
如果一個對象具有強引用,垃圾回收器不會回收該對象,當記憶體空間不足時,JVM 寧願拋出 OutOfMemoryError異常。
如果強引用對象不使用時,需要弱化從而使GC能夠回收,如下:
//幫助垃圾收集器回收此對象 mikechen=null;
顯式地設置mikechen對象為null,或讓其超出對象的生命周期範圍,則GC認為該對象不存在引用,這時就可以回收這個對象,具體什麼時候收集這要取決於GC演算法。
舉例:
package com.mikechen.java.refenence; /** * 強引用舉例 * * @author mikechen */ public class StrongRefenenceDemo { public static void main(String[] args) { Object o1 = new Object(); Object o2 = o1; o1 = null; System.gc(); System.out.println(o1); //null System.out.println(o2); //java.lang.Object@2503dbd3 } }
StrongRefenenceDemo 中儘管 o1已經被回收,但是 o2 強引用 o1,一直存在,所以不會被GC回收。
軟引用
軟引用是一種相對強引用弱化了一些的引用,需要用java.lang.ref.SoftReference 類來實現。
比如:
String str=new String("abc"); // 強引用 SoftReference<String> softRef=new SoftReference<String>(str); // 軟引用
如果一個對象只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它,如果記憶體空間不足了,就會回收這些對象的記憶體。
先通過一個例子來瞭解一下軟引用:
/** * 弱引用舉例 * * @author mikechen */ Object obj = new Object(); SoftReference softRef = new SoftReference<Object>(obj);//刪除強引用 obj = null;//調用gc // 對象依然存在 System.gc();System.out.println("gc之後的值:" + softRef.get());
軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用對象被垃圾回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
ReferenceQueue<Object> queue = new ReferenceQueue<>(); Object obj = new Object(); SoftReference softRef = new SoftReference<Object>(obj,queue);//刪除強引用 obj = null;//調用gc System.gc(); System.out.println("gc之後的值: " + softRef.get()); // 對象依然存在 //申請較大記憶體使記憶體空間使用率達到閾值,強迫gc byte[] bytes = new byte[100 * 1024 * 1024];//如果obj被回收,則軟引用會進入引用隊列 Reference<?> reference = queue.remove();if (reference != null){ System.out.println("對象已被回收: "+ reference.get()); // 對象為null }
軟引用通常用在對記憶體敏感的程式中,比如高速緩存就有用到軟引用,記憶體夠用的時候就保留,不夠用就回收。
我們看下 Mybatis 緩存類 SoftCache 用到的軟引用:
public Object getObject(Object key) { Object result = null; SoftReference<Object> softReference = (SoftReference)this.delegate.getObject(key); if (softReference != null) { result = softReference.get(); if (result == null) { this.delegate.removeObject(key); } else { synchronized(this.hardLinksToAvoidGarbageCollection) { this.hardLinksToAvoidGarbageCollection.addFirst(result); if (this.hardLinksToAvoidGarbageCollection.size() > this.numberOfHardLinks) { this.hardLinksToAvoidGarbageCollection.removeLast(); } } } } return result;}
註意:軟引用對象是在jvm記憶體不夠的時候才會被回收,我們調用System.gc()方法只是起通知作用,JVM什麼時候掃描回收對象是JVM自己的狀態決定的,就算掃描到軟引用對象也不一定會回收它,只有記憶體不夠的時候才會回收。
弱引用
弱引用的使用和軟引用類似,只是關鍵字變成了 WeakReference:
MikeChen mikechen = new MikeChen(); WeakReference<MikeChen> wr = new WeakReference<MikeChen>(mikechen );
弱引用的特點是不管記憶體是否足夠,只要發生 GC,都會被回收。
舉例說明:
public class WeakHashMapDemo { public static void main(String[] args) throws InterruptedException { myHashMap(); myWeakHashMap(); } public static void myHashMap() { HashMap<String, String> map = new HashMap<String, String>(); String key = new String("k1"); String value = "v1"; map.put(key, value); System.out.println(map); key = null; System.gc(); System.out.println(map); } public static void myWeakHashMap() throws InterruptedException { WeakHashMap<String, String> map = new WeakHashMap<String, String>(); //String key = "weak"; // 剛開始寫成了上邊的代碼 //思考一下,寫成上邊那樣會怎麼樣? 那可不是引用了 String key = new String("weak"); String value = "map"; map.put(key, value); System.out.println(map); //去掉強引用 key = null; System.gc(); Thread.sleep(1000); System.out.println(map); }}
弱引用的應用
WeakHashMap
public class WeakHashMapDemo { public static void main(String[] args) throws InterruptedException { myHashMap(); myWeakHashMap(); } public static void myHashMap() { HashMap<String, String> map = new HashMap<String, String>(); String key = new String("k1"); String value = "v1"; map.put(key, value); System.out.println(map); key = null; System.gc(); System.out.println(map); } public static void myWeakHashMap() throws InterruptedException { WeakHashMap<String, String> map = new WeakHashMap<String, String>(); //String key = "weak"; // 剛開始寫成了上邊的代碼 //思考一下,寫成上邊那樣會怎麼樣? 那可不是引用了 String key = new String("weak"); String value = "map"; map.put(key, value); System.out.println(map); //去掉強引用 key = null; System.gc(); Thread.sleep(1000); System.out.println(map); }}
當key只有弱引用時,GC發現後會自動清理鍵和值,作為簡單的緩存表解決方案。
ThreadLocal
static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } //......}
ThreadLocal.ThreadLocalMap.Entry 繼承了弱引用,key為當前線程實例,和WeakHashMap基本相同。
虛引用
虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定對象的生命周期。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。
虛引用也稱為“幽靈引用”或者“幻影引用”,它是最弱的一種引用關係。
虛引用需要java.lang.ref.PhantomReference 來實現:
A a = new A(); ReferenceQueue<A> rq = new ReferenceQueue<A>(); PhantomReference<A> prA = new PhantomReference<A>(a, rq);
虛引用主要用來跟蹤對象被垃圾回收器回收的活動。
虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列 (ReferenceQueue)聯合使用,當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的記憶體之前,把這個虛引用加入到與之 關聯的引用隊列中。
Java引用總結
java4種引用的級別由高到低依次為:強引用 > 軟引用 > 弱引用 > 虛引用。
以上
作者簡介
陳睿|mikechen,10年+大廠架構經驗,《BAT架構技術500期》系列文章作者,分享十餘年BAT架構經驗以及面試心得!
閱讀mikechen的互聯網架構更多技術文章合集
Java併發|JVM|MySQL|Spring|Redis|分散式|高併發|架構師