在Java1.2中我們可以發現一個java.lang.ref包,在這個包中我們可以發現有關引用的知識,比如WeakReference弱引用和SoftReference強引用。
弱引用(WeakReference):
隻具有弱引用的對象聲明周期更短暫,在垃圾回收期線程掃描它所管轄的記憶體區域的過程中,一旦發現了隻具有若引用的對象,不管目前記憶體空間是否足夠,都會回收它的記憶體,不過,要注意的是,由于垃圾回收期是一個優先級很低的線程,是以不一定會很快發現那些隻具有弱引用的對象。
軟引用(SoftReference):
也稱為強引用,如果一個對象隻具有軟引用,則記憶體空間足夠,垃圾回收期就不會回收它,如果記憶體空間不足了,就會回收這些對象的記憶體,如果垃圾回收期沒有回收它,該對象就可以被程式使用。
WeakReference和SoftReference的用武之地:
WeakReference通常用于在某處儲存對象的引用,而又不幹擾該對象被GC回收,如:用于Debug、記憶體監視工具等程式中。因為這類程式一般要求即要觀察到對象,又不能影響該對象正常的GC過程。
SoftReference是強引用,它儲存的對象執行個體,除非JVM即将OutOfMemory,否則不會被GC回收。這個特性使得它特别适合設計對象Cache。對于Cache,我們希望被緩存的對象最好始終常駐記憶體,但是如果JVM記憶體吃緊,為了不發生OutOfMemoryError導緻系統崩潰,必要的時候也允許JVM回收Cache的記憶體,待後續合适的時機再把資料重新Load到Cache中。這樣可以系統設計得更具彈性。
下面通過一個執行個體,來展現這兩種引用如何儲存對象執行個體的:
User.java:
public class User implements Serializable{
private static final long serialVersionUID = 1L;
/* 使用者id */
private Integer uid;
/* 使用者名 */
private String uname;
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
}
WeakReferenceTest.java:
public class WeakReferenceTest {
public static void main(String[] args) {
/*建立User對象*/
User user = new User();
/*設定username*/
user.setUname("廖澤民");
/*把對象放在弱引用中*/
WeakReference<User> weak = new WeakReference<>(user);
/*把user對象置空,然後再從若引用中取值*/
user = null;
int i = 0;
/*weak.get()表示從引用中取得對象*/
while (weak.get() != null) {
System.out.println(String.format("從弱引用中取值: %s, count: %d", weak.get().getUname(), ++i));
if (i % 10 == 0) {
System.gc();
System.out.println("記憶體回收方法被調用");
}
try {
Thread.sleep(500);
} catch (Exception e) {
}
}
System.out.println("對象已經被JVM回收");
}
}
運作的結果:
從運作結果,我們可以發現當把對象執行個體儲存到WeakReference後,再将對象置空,然後從WeakReference中取值,當System.gc()方法被調用後,對象執行個體也會被回收!
SoftReferenceTest.java:
public class SoftReferenceTest {
public static void main(String[] args) {
/* 建立User對象 */
User user = new User();
/* 設定使用者名 */
user.setUname("廖澤民");
/* 建立強引用對象 */
SoftReference<User> soft = new SoftReference<User>(user);
/* 把user對象置空,然後再從強引用中取值【注:要先存在引用中再置空,注意順序啊】 */
user = null;
int i = 0;
while (soft.get() != null) {
System.out.println(String.format("從強引用中擷取對象: %s, count: %d", soft.get().getUname(), ++i));
if (i % 10 == 0) {
System.gc();
System.out.println("記憶體回收方法被調用!");
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
System.out.println("對象已經被JVM回收!");
}
}
運作結果:
從運作結果(程式不會停止,一直執行)可以發現,我們把對象執行個體儲存到SoftReference中,然後将對象置空,再從SoftReference中取值時,即使顯示的調用System.gc();方法,該對象執行個體也不會被回收(除非發生記憶體溢出,該對象才會被回收)