強引用:=
軟引用:SoftReference
弱引用:WeakReference
虛引用:PhantomReference
對象的可及性:
強可及對象:永遠都不會被GC回收,除非OOM
軟可及對象:當系統內存不足的時候,被GC回收
弱可及對象:當系統GC發現這個對象,就被回收
虛可及對象:虛引用用來檢測對象是否被回收
引用隊列(ReferenceQueue):
弱引用,軟引用,虛引用中都存在兩種構造器,第二種可以傳一個引用隊列,如果我們使用第二種構造參數創建引用對象時,這個引用對象就會被監聽,一旦有對象被回收,所指向它的引用對象就會被添加到引用隊列中,開發者可以把這個引用從隊列中彈出,來判斷對象是否被回收。
代碼展示:
這裏以String對象爲例:
創建一個String對象,分別用強引用,弱引用,軟引用指向這個對象
String string2=new String("abc");
//創建一個軟引用,讓它指向string2對象
SoftReference<String> sfr=new SoftReference<String>(string2);
//創建一個弱引用,讓它指向string2對象
WeakReference<String> wrf=new WeakReference<String>(string2);
注意這裏不能寫成:String str="abc",因爲"abc"是放在常量區中的,而gc是從堆內存中找垃圾對象。所以“abc”不會被回收
首先,我們刪除強引用,主動gc,並打印軟引用和弱引用的值:
string2=null; //去掉強引用
System.gc();
System.out.println("軟引用所引用對象的值:"+sfr.get());
System.out.println("弱引用所引用對象的值:"+wrf.get());
打印結果:
軟引用所引用對象的值:abc
弱引用所引用對象的值:abc
我們發現刪除強引用後,開啓gc回收,gc並不會回收被軟引用指向的對象。
然後,我們把軟引用清空,再次主動gc,並打印弱引用的值
sfr.clear();
System.gc();
System.out.println(wrf.get());//對於弱可及對象,當系統GC發現這個對象,就被回收
打印結果:
null
我們發現當對象只剩下弱引用時,只要gc開始回收,這個對象就會被當成垃圾對象回收。
最後,我在這裏寫一下引用隊列監聽對象回收的測試:
private static void ReferenceQueueTest() {
final ReferenceQueue<String> QUEUE=new ReferenceQueue<>();
String str=new String("abc");
//虛引用用來檢測對象是否被回收
PhantomReference<String> prf=new PhantomReference<String>(str,QUEUE);
WeakReference<String> wrf=new WeakReference<String>(str,QUEUE);
new Thread(new Runnable() {
@Override
public void run() {
while(true){
Reference<? extends String> poll = QUEUE.poll();
if(poll!=null){ //對象被回收
System.out.println("--- 引用對象被回收 ---- " + poll);
System.out.println("--- 回收對象 ---- " + poll.get());
}
}
}
}).start();
try {
Thread.sleep(2000); //這裏模擬對象的生存時間,兩秒後強引用被刪除,對象被回收
} catch (InterruptedException e) {
e.printStackTrace();
}
str=null; //刪除強引用
System.gc(); //主動gc
}
打印結果:
--- 引用對象被回收 ---- java.lang.ref.WeakReference@513065ba
--- 回收對象 ---- null
--- 引用對象被回收 ---- java.lang.ref.PhantomReference@5ff781ec
--- 回收對象 ---- null
我這裏用了一個子線程來等待引用對象進入引用隊列,我們將String對象的強引用刪除,然後主動gc,這樣子String對象就會被回收。回收後,上面測試中的弱引用對象和虛引用對象就都被放入了引用隊列中。我們將引用對象彈出隊列,就可以檢查String對象是否被回收了。