強引用
? ? 像“Object obj = new Object()”這類的引用均為強引用,當一個對象被強引用變量引用時,它處于可達狀態,是不可能被垃圾回收器回收的,即使該對象永遠不會被用到也不會被回收。
? ? 當JVM出現內存不足時,JVM進行垃圾回收,對于強引用的對象,就算是出現了 OOM 也不會對該對象進行回收,打死都不收。因此強引用有時也是造成 Java 內存泄露的原因之一。如果將其引用賦值為null,一般認為是可以被垃圾回收掉的。(具體回收時間還要看使用的GC策略)
? ? 示例代碼:
public class StrongRefenenceDemo {public static void main(String[] args) {Object o1 = new Object();Object o2 = o1;o1 = null;System.gc();System.out.println(o1); //nullSystem.out.println(o2); //java.lang.Object@2503dbd3}
}
軟引用
?? ?軟引用是一種相對強引用弱化了一些的引用,需要用java.lang.ref.SoftReference 類來實現,可以讓對象豁免一些垃圾收集。
?? ?軟引用用來描述一些還有用,但并非必需的對象。
? ? 對于只有軟引用的對象:當系統內存充足時它不會被回收,當系統內存不足時它才會被回收。
//VM options: -Xms5m -Xmx5m
public class SoftRefenenceDemo {public static void main(String[] args) {softRefMemoryEnough();System.out.println("------內存不夠用的情況------");softRefMemoryNotEnough();}private static void softRefMemoryEnough() {Object o1 = new Object();SoftReference<Object> s1 = new SoftReference<Object>(o1);System.out.println(o1);System.out.println(s1.get());o1 = null;System.gc();System.out.println(o1);System.out.println(s1.get());}/*** JVM配置`-Xms5m -Xmx5m` ,然后故意new一個一個大對象,使內存不足產生 OOM,看軟引用回收情況*/private static void softRefMemoryNotEnough() {Object o1 = new Object();SoftReference<Object> s1 = new SoftReference<Object>(o1);System.out.println(o1);System.out.println(s1.get());o1 = null;byte[] bytes = new byte[10 * 1024 * 1024];System.out.println(o1);System.out.println(s1.get());}
}
輸出結果:
--------------------------------------輸出結果-------------------------------------------------
java.lang.Object@2503dbd3
java.lang.Object@2503dbd3
null
java.lang.Object@2503dbd3
------內存不夠用的情況------
java.lang.Object@4b67cf4d
java.lang.Object@4b67cf4d
java.lang.OutOfMemoryError: Java heap space
at reference.SoftRefenenceDemo.softRefMemoryNotEnough(SoftRefenenceDemo.java:42)
at reference.SoftRefenenceDemo.main(SoftRefenenceDemo.java:15)
null
null
弱引用
?? ?弱引用也是用來描述非必需對象的,但是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生之前。當垃圾收集器工作時,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。
?? ?弱引用需要用java.lang.ref.WeakReference類來實現,它比軟引用的生存期更短。
public class WeakReferenceDemo {public static void main(String[] args) {Object o1 = new Object();WeakReference<Object> w1 = new WeakReference<Object>(o1);System.out.println(o1);System.out.println(w1.get());o1 = null;System.gc();System.out.println(o1);//均被回收System.out.println(w1.get());//均被回收}
}
關于WeakHashMap示例代碼
public class WeakHashDemo {public static void main(String[] args) throws InterruptedException {myHashMap();System.out.println("-----------------------------------------------");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);}
}
----------------------------------輸出結果--------------------------------------------
{k1=v1}
{k1=v1}
-----------------------------------------------
{weak=map}
{}
?
虛引用
?? ?虛引用也稱為“幽靈引用”或者“幻影引用”,它是最弱的一種引用關系。虛引用需要java.lang.ref.PhantomReference 來實現。
?? ?虛引用需要java.lang.ref.PhantomReference 來實現。
?? ?如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收,它不能單獨使用也不能通過它訪問對象,虛引用必須和引用隊列(RefenenceQueue)聯合使用。
?? ?虛引用的主要作用是跟蹤對象垃圾回收的狀態。僅僅是提供了一種確保對象被 finalize 以后,做某些事情的機制。
?? ?PhantomReference 的 get 方法總是返回 null,因此無法訪問對應的引用對象。其意義在于說明一個對象已經進入 finalization 階段,可以被 GC 回收,用來實現比 finalization 機制更靈活的回收操作。
?? ?設置虛引用的唯一目的,就是在這個對象被回收器回收的時候收到一個系統通知或者后續添加進一步的處理。
?
public class PhantomReferenceDemo {public static void main(String[] args) throws InterruptedException {Object o1 = new Object();ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();PhantomReference<Object> phantomReference = new PhantomReference<Object>(o1,referenceQueue);System.out.println(o1);System.out.println(referenceQueue.poll());System.out.println(phantomReference.get());o1 = null;System.gc();Thread.sleep(3000);System.out.println(o1);System.out.println(referenceQueue.poll()); //引用隊列中System.out.println(phantomReference.get());}}
-------------------------------------------------------------------------------------------------------
java.lang.Object@4554617c
null
null
null
java.lang.ref.PhantomReference@74a14482
null
引用隊列
?? ?ReferenceQueue 是用來配合引用工作的,沒有ReferenceQueue 一樣可以運行。
?? ?SoftReference、WeakReference、PhantomReference 都有一個可以傳遞 ReferenceQueue 的構造器。
?? ?創建引用的時候,可以指定關聯的隊列,當 GC 釋放對象內存的時候,會將引用加入到引用隊列。如果程序發現某個虛引用已經被加入到引用隊列,那么就可以在所引用的對象的內存被回收之前采取必要的行動,這相當于是一種通知機制。
Reference的四種狀態:
-
Active:新創建的引用實例處于Active狀態,但當GC檢測到該實例引用的實際對象的可達性發生某些改變(實際對象處于 GC roots 不可達)后,它的狀態將變化為Pending或者Inactive。如果 Reference 注冊了ReferenceQueue,則會切換為Pending,并且Reference會加入pending-Reference鏈表中,如果沒有注冊ReferenceQueue,會切換為Inactive。
-
Pending:當引用實例被放置在pending-Reference 鏈表中時,它處于Pending狀態。此時,該實例在等待一個叫Reference-handler的線程將此實例進行enqueue操作。如果某個引用實例沒有注冊在一個引用隊列中,該實例將永遠不會進入Pending狀態。
-
Enqueued:在ReferenceQueue隊列中的Reference的狀態,如果Reference從隊列中移除,會進入Inactive狀態。
-
Inactive:一旦某個引用實例處于Inactive狀態,它的狀態將不再會發生改變,同時說明該引用實例所指向的實際對象一定會被GC所回收。
?想要了解更多技術文章請關注公眾號“職谷智享”,每天關注的前50名小伙伴可領取200+互聯網相關技術書籍。
?