這個問題是阿里菜鳥網絡一面面試官的提問,由於個人準備不夠充分以及知識儲備不足沒有答出問題根本。其實這個問題是Java面試中比較常見的問題,按難度來劃分的話應該只是中等難度,之前也瞭解過它們的區別,但是實際開發過程幾乎很少使用導致只是純粹記憶,本文將系統的整理強引用,軟引用,弱引用和虛引用的區別,並結合具體場景探究一下它們的用法。
一、什麼是強引用,軟引用,弱引用,虛引用?
在 JDK1.2 以前,如果一個對象不被任何變量引用,則程序無法再次使用這個對象,這個對象最終會被 GC(GabageCollection:垃圾回收)。但是如果之後可能還會用到這個對象,就只能去新建一個了,這其實就降低了 JVM 性能,沒有達到最大的優化策略。
從JDK1.2開始,提供了四種類型的引用:強引用(StrongReference)、軟引用(SoftReference)、弱引用(WeakReference)和虛引用(PhantomReference)。主要有兩個目的:1.可以在代碼中決定某些對象的生命週期;2.優化JVM的垃圾回收機制。
1.強引用(StrongReference)
強引用是程序代碼之中普遍存在的,我們一般創建對象的過程都是強引用,如下面代碼。
Object object = new Object();
String str = "123";
如果某個對象有強引用與之關聯,Java虛擬機(JVM)必定不會回收這個對象,即使在內存不足的情況下,JVM寧願拋出OutOfMemory 錯誤也不會回收這種對象。基於上述情況,我們在使用完對象後如果想讓 JVM 回收對象需要將對象弱化,具體操作是將其引用置爲null。(補充說明:局部方法內的強引用會隨着方法的結束退棧而自動清除引用,全局變量需要在不使用時置null)
Object strongReference = new Object();
strongReference = null;
Java集合類的強引用弱化方式不能採取置 null 方式,因爲這種方式強引用依然存在,只有採用 clear()
方法內存數組中存放的引用類型進行內存釋放,這樣纔可以及時釋放內存。
ArrayList<Integer> arrays = new ArrayList<>(10);
arrays.add(1);
arrays.add(2);
arrays.add(3);
arrays.clear();//不使用時 clear 清除數組
查看 ArrayList 的 clear 方法的源碼發現是遍歷元素數組將每個元素置爲 null ,即 elementData[i] = null 。
2.軟引用(SoftReference)
軟引用是用來描述一些有用但並不是必需的對象,適合用來實現緩存(比如瀏覽器的‘後退’按鈕使用的緩存),JVM內存空間充足的時候將數據緩存在內存中,如果空間不足了就將其回收掉。
// 強引用
String strongReference = new String("123");
// 軟引用,"456"這個String對象包含一個強引用 str 和弱引用 softReference
String str = new String("456");
SoftReference<String> softReference = new SoftReference<String>(str);
軟引用可以和一個引用隊列(ReferenceQueue
)聯合使用。如果軟引用所引用對象被垃圾回收,JAVA
虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
String str = new String("abc");
SoftReference<String> softReference = new SoftReference<>(str, referenceQueue);
str = null;//去掉 str 的強引用
System.gc();// Notify GC
System.out.println(softReference.get()); // abc
Reference<? extends String> reference = referenceQueue.poll();
System.out.println(reference); //null
軟引用對象是在jvm內存不夠的時候纔會被回收,我們調用System.gc()方法只是起通知作用,JVM什麼時候掃描回收對象是JVM自己的狀態決定的。即使掃描到軟引用對象也不一定會回收它,只有內存不夠的時候JVM纔會回收。
3.弱引用(WeakReference)
弱引用也是用來描述非必需對象的,當JVM進行垃圾回收時,無論內存是否充足,都會回收被弱引用關聯的對象,在java中用java.lang.ref.WeakReference類來表示。
弱引用與軟引用的區別在於:只具有弱引用的對象擁有更短暫的生命週期,它只能生存到下一次垃圾收集發生之前。當垃圾回收器掃描到只具有弱引用的對象時,無論當前內存空間是否足夠,都會回收它。不過,由於垃圾回收器是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。
String str = new String("abc");
WeakReference<String> weakReference = new WeakReference<>(str);
str = null;//去掉強引用
如果一個對象是偶爾(很少)的使用,並且希望在使用時隨時就能獲取到,但又不想影響此對象的垃圾收集,那麼我們應該用Weak Reference來標記此對象。
弱引用也可以和一個引用隊列(ReferenceQueue
)聯合使用,如果弱引用所引用的對象被垃圾回收,Java
虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。
String person = new String ("123");
ReferenceQueue<Person> queue = new ReferenceQueue<>();
WeakReference<Person> weakReference = new WeakReference<Person>(person, queue);
person = null;//去掉強引用
4.虛引用(PhantomReference)
與其他三種引用都不同,虛引用並不會決定對象的生命週期。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。
虛引用主要用來跟蹤對象被垃圾回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃 圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。
String str= new String();
ReferenceQueue queue = new ReferenceQueue ();
// 創建虛引用,要求必須與一個引用隊列關聯
PhantomReference pr = new PhantomReference (str, queue);
虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。
程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要進行垃圾回收。如果程序發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前採取必要的行動。