首先來看一下我們在Java中最常見的操作,new一個對象,在記憶體中是如何進行的,來看一下我畫的草圖:

如上圖所示,我們建立一個對象并用一個引用去指向它共用了3步:
1. new A():
在堆記憶體(heap)中建立一個A類的對象(或叫執行個體)。
2. A a:
在棧記憶體(stack)中聲明一個指向A類型對象的引用(可以簡單了解為C中的指針,裡面儲存的是對象在堆中的位址),但這時它并沒有指向任何對象,隻是聲明了,就是告訴JVM"我可以指向A類型的對象"。
3. A a = new A();
這一步就完成了将棧記憶體中的引用a指向堆記憶體中的對象new A()。
好了,經過上面的描述,我們了解了平時去new一個對象并用一個引用去指向它的過程是怎麼樣的了。引用也有不同的類型:強引用,弱引用, 虛引用, 軟引用。關于這些不同類型引用的具體差别可以參考海子大神的部落格:http://www.cnblogs.com/dolphin0520/p/3784171.html
這裡我隻說明弱引用(WeakReference),那什麼叫弱引用呢?
我們知道Java差別于C/C++的一個重要不同點就是Java不需要程式員手動申請或釋放記憶體,JVM通過GC(Garbage-Colletion)(GC實際是一條專門使用者回收Java堆記憶體中無用對象的線程)幫我們完成了垃圾的回收操作,是以程式員不必擔心記憶體的釋放問題,也就是不用手動管理對象的生命周期了。
但這也并不表示說Java程式員完全不必擔心記憶體問題了,比方說我們new了很多對象,而每個對象的生命周期又非常的長,長到就算記憶體不足了GC也沒辦法去回收掉一些對象來釋放更多的記憶體,這樣最終就會導緻OOM(Out-Of-Memory)錯誤。
是以如果你希望能夠控制一些對象的生命周期,比方說當JVM記憶體不足時這些對象就可以被回收,或者說隻要GC線程掃描到該對象,那麼這個對象就會被回收。
那怎樣控制這些對象的生命周期呢?答案就是通過指定指向對象的引用的類型!
不同類型的引用所指向對象的生命周期是不同的。
那弱引用所指向的對象的生命周期是怎樣的呢?可以看下我畫的圖:

圖中,左側是GC線程;中間是存儲對象執行個體的堆;右側是存放引用的棧。
當JVM啟動GC線程進行記憶體回收時,GC線程會掃描堆中的對象,當掃描到對象2的時候發現指向它的隻有一個弱引用!好嘛,二話不說就把它回收了!根本不管weakReference的哀嚎!真是人弱被人欺啊,哈哈~
是以,弱引用指向的對象的生命周期就是:祈禱别被GC掃描到!
JVM通過GC進行垃圾回收時,當GC掃描到被弱引用指向的對象時就會将該對象回收掉(僅有弱引用指向該對象),特别霸氣!
特别強調:
JVM之是以在回收被弱引用指向對象時這麼“猖狂”,是因為被回收的對象隻有一個弱引用指向它,如果同時還有強引用指向目前對象的話JVM可是沒這個膽子的哈。
請看我下面的代碼示範:
import java.lang.ref.WeakReference;
public class TestWeakReference {
public static void main(String[] args) {
// 初始化一個弱引用,該弱引用指向String對象 ( new String("hello") )
WeakReference weak = new WeakReference(new String("hello"));
System.out.println("before gc, weak.get() = " + weak.get());
// 第11行在這!!! 聲明一個強引用也指向String對象
//String strong = weak.get();
System.gc();// 通知gc進行垃圾回收,但不一定會立即響應,是以讓主線程睡眠3秒等待gc線程執行
try {
Thread.sleep(3000);//
}catch(Exception e) {
e.printStackTrace();
}
System.out.println("after gc, weak.get() = " + weak.get());
}
}
列印結果:
1、注釋第11行代碼時:
*before gc, weak.get() = hello *
after gc, weak.get() = null
當我們注釋掉第11行代碼時,我們發現上面列印的結果“after gc, weak.get() = null”,也就是說經過GC操作之後被弱引用指向的對象被回收掉了。
這時因為指向String對象的隻有一個弱引用weak,這時GC回收掃描到String對象會毫不猶豫的回收掉String對象。如下圖:

2、放開第11行代碼時:
*before gc, weak.get() = hello *
after gc, weak.get() = hello
當我們放開第11行代碼時,我們發現了神奇的事情:“after gc, weak.get() = hello”,也就是說,雖然GC掃描到了被弱引用weak指向的對象,但是卻并沒有把該對象回收掉(大家如果擔心可能是GC線程還沒執行導緻的原因,可以把主線程睡眠時間設定長一點,多做幾次試驗哈)。
這是因為指向String對象的引用有兩個:一個弱引用weak、一個強應用strong。這時GC可就不敢回收該String對象了,因為同時有一個強引用指向了它。如下圖:
