本文轉載自:博客園 skywang12345用戶的
Java引用總結–StrongReference、SoftReference、WeakReference、PhantomReference
感謝分享。
一、Java的引用介紹
Java從1.2版本開始引入了4種引用,這4種引用的級別由高到低依次爲:
強引用 > 軟引用 > 弱引用 > 虛引用
⑴強引用(StrongReference)
強引用是使用最普遍的引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足的問題。
⑵軟引用(SoftReference)
如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高速緩存。
軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
⑶弱引用(WeakReference)
弱引用與軟引用的區別在於:只具有弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由於垃圾回收器是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。
弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。
⑷虛引用(PhantomReference)
“虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定對象的生命週期。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。
虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之 關聯的引用隊列中。
由於引用和內存回收關係緊密。下面,先通過實例對內存回收有個認識;然後,進一步通過引用實例加深對引用的瞭解。
二、內存回收
創建公共類MyDate,它的作用是覆蓋finalize()函數:在finalize()中輸出打印信息,方便追蹤。
說明:finalize()函數是在JVM回收內存時執行的,但JVM並不保證在回收內存時一定會調用finalize()。
MyDate代碼如下:
package com.skywang.java;
import java.util.Date;
public class MyDate extends Date {
/** Creates a new instance of MyDate */
public MyDate() {
}
// 覆蓋finalize()方法
protected void finalize() throws Throwable {
super.finalize();
System.out.println("obj [Date: " + this.getTime() + "] is gc");
}
public String toString() {
return "Date: " + this.getTime();
}
}
在這個類中,對java.util.Date類進行了擴展,並重寫了finalize()和toString()方法。
創建公共類ReferenceTest,它的作用是定義一個方法drainMemory():消耗大量內存,以此來引發JVM回收內存。
ReferenceTest代碼如下:
package com.skywang.java;
public class ReferenceTest {
/** Creates a new instance of ReferenceTest */
public ReferenceTest() {
}
// 消耗大量內存
public static void drainMemory() {
String[] array = new String[1024 * 10];
for(int i = 0; i < 1024 * 10; i++) {
for(int j = 'a'; j <= 'z'; j++) {
array[i] += (char)j;
}
}
}
}
在這個類中定義了一個靜態方法drainMemory(),此方法旨在消耗大量的內存,促使JVM運行垃圾回收。
有了上面兩個公共類之後,我們即可測試JVM什麼時候進行垃圾回收。下面分3種情況進行測試:
情況1:清除對象
實現代碼:
package com.skywang.java;
public class NoGarbageRetrieve {
public static void main(String[] args) {
MyDate date = new MyDate();
date = null;
}
}
運行結果:
<無任何輸出>
結果分析:date雖然設爲null,但由於JVM沒有執行垃圾回收操作,MyDate的finalize()方法沒有被運行。
情況2:顯式調用垃圾回收
實現代碼:
package com.skywang.java;
public class ExplicitGarbageRetrieve {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
MyDate date = new MyDate();
date = null;
System.gc();
}
}
運行結果:
obj [Date: 1372137067328] is gc
結果分析:調用了System.gc(),使JVM運行垃圾回收,MyDate的finalize()方法被運行。
情況3:隱式調用垃圾回收
實現代碼:
package com.skywang.java;
public class ImplicitGarbageRetrieve {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
MyDate date = new MyDate();
date = null;
ReferenceTest.drainMemory();
}
}
運行結果:
obj [Date: 1372137171965] is gc
結果分析:雖然沒有顯式調用垃圾回收方法System.gc(),但是由於運行了耗費大量內存的方法,觸發JVM進行垃圾回收。
總結:JVM的垃圾回收機制,在內存充足的情況下,除非你顯式調用System.gc(),否則它不會進行垃圾回收;在內存不足的情況下,垃圾回收將自動運行
三、Java對引用的分類
3.1 強引用
實例代碼:
package com.skywang.java;
public class StrongReferenceTest {
public static void main(String[] args) {
MyDate date = new MyDate();
System.gc();
}
}
運行結果:
<無任何輸出>
結果說明:即使顯式調用了垃圾回收,但是用於date是強引用,date沒有被回收。
3.2 軟引用
實例代碼:
package com.skywang.java;
import java.lang.ref.SoftReference;
public class SoftReferenceTest {
public static void main(String[] args) {
SoftReference ref = new SoftReference(new MyDate());
ReferenceTest.drainMemory();
}
}
運行結果:
<無任何輸出>
結果說明:在內存不足時,軟引用被終止。軟引用被禁止時。
SoftReference ref = new SoftReference(new MyDate());
ReferenceTest.drainMemory();
等價於
MyDate date = new MyDate();
// 由JVM決定運行
If(JVM.內存不足()) {
date = null;
System.gc();
}
3.3 弱引用
示例代碼:
package com.skywang.java;
import java.lang.ref.WeakReference;
public class WeakReferenceTest {
public static void main(String[] args) {
WeakReference ref = new WeakReference(new MyDate());
System.gc();
}
}
運行結果:
obj [Date: 1372142034360] is gc
結果說明:在JVM垃圾回收運行時,弱引用被終止。
WeakReference ref = new WeakReference(new MyDate());
System.gc();
等同於:
MyDate date = new MyDate();
// 垃圾回收
If(JVM.內存不足()) {
date = null;
System.gc();
}
3.4 假象引用
示例代碼:
package com.skywang.java;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.PhantomReference;
public class PhantomReferenceTest {
public static void main(String[] args) {
ReferenceQueue queue = new ReferenceQueue();
PhantomReference ref = new PhantomReference(new MyDate(), queue);
System.gc();
}
}
運行結果:
obj [Date: 1372142282558] is gc
結果說明:假象引用,在實例化後,就被終止了。
ReferenceQueue queue = new ReferenceQueue();
PhantomReference ref = new PhantomReference(new MyDate(), queue);
System.gc();
等同於:
MyDate date = new MyDate();
date = null;
可以用以下表格總結上面的內容:
級別 | 什麼時候被垃圾回收 | 用途 | 生存時間 |
---|---|---|---|
強引用 | 從來不會 | 對象的一般狀態 | JVM停止運行時終止 |
軟引用 | 在內存不足時 | 對象簡單?緩存 | 內存不足時終止 |
弱引用 | 在垃圾回收時 | 對象緩存 | gc運行後終止 |
虛引用 | Unknown | Unknown | Unknown |