一、背景
Java的記憶體回收不需要程式設計師負責,JVM會在必要時啟動Java GC完成垃圾回收。Java以便我們控制物件的生存週期,提供給了我們四種引用方式,引用強度從強到弱分別為:強引用、軟引用、弱引用、虛引用。
二、簡介
1.強引用 StrongReference
StrongReference是Java的預設引用形式,使用時不需要顯示定義。任何通過強引用所使用的物件不管系統資源有多緊張,Java GC都不會主動回收具有強引用的物件。
public class StrongReferenceTest {
public static int M = 1024*1024;
public static void printlnMemory(String tag){
Runtime runtime = Runtime.getRuntime();
int M = StrongReferenceTest.M;
System.out.println("\n"+tag+":");
System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
}
public static void main(String[] args){
StrongReferenceTest.printlnMemory("1.原可用記憶體和總記憶體");
//例項化10M的陣列並與strongReference建立強引用
byte[] strongReference = new byte[10*StrongReferenceTest.M];
StrongReferenceTest.printlnMemory("2.例項化10M的陣列,並建立強引用");
System.out.println("strongReference : "+strongReference);
System.gc();
StrongReferenceTest.printlnMemory("3.GC後");
System.out.println("strongReference : "+strongReference);
//strongReference = null;後,強引用斷開了
strongReference = null;
StrongReferenceTest.printlnMemory("4.強引用斷開後");
System.out.println("strongReference : "+strongReference);
System.gc();
StrongReferenceTest.printlnMemory("5.GC後");
System.out.println("strongReference : "+strongReference);
}
}
複製程式碼
執行結果:
2.弱引用 WeakReference
如果一個物件只具有弱引用,無論記憶體充足與否,Java GC後物件如果只有弱引用將會被自動回收。
public class WeakReferenceTest {
public static int M = 1024*1024;
public static void printlnMemory(String tag){
Runtime runtime = Runtime.getRuntime();
int M = WeakReferenceTest.M;
System.out.println("\n"+tag+":");
System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
}
public static void main(String[] args){
WeakReferenceTest.printlnMemory("1.原可用記憶體和總記憶體");
//建立弱引用
WeakReference<Object> weakRerference = new WeakReference<Object>(new byte[10*WeakReferenceTest.M]);
WeakReferenceTest.printlnMemory("2.例項化10M的陣列,並建立弱引用");
System.out.println("weakRerference.get() : "+weakRerference.get());
System.gc();
StrongReferenceTest.printlnMemory("3.GC後");
System.out.println("weakRerference.get() : "+weakRerference.get());
}
}
複製程式碼
執行結果:
3.軟引用 SoftReference
軟引用和弱引用的特性基本一致, 主要的區別在於軟引用在記憶體不足時才會被回收。如果一個物件只具有軟引用,Java GC在記憶體充足的時候不會回收它,記憶體不足時才會被回收。
public class SoftReferenceTest {
public static int M = 1024*1024;
public static void printlnMemory(String tag){
Runtime runtime = Runtime.getRuntime();
int M = StrongReferenceTest.M;
System.out.println("\n"+tag+":");
System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
}
public static void main(String[] args){
SoftReferenceTest.printlnMemory("1.原可用記憶體和總記憶體");
//建立軟引用
SoftReference<Object> softRerference = new SoftReference<Object>(new byte[10*SoftReferenceTest.M]);
SoftReferenceTest.printlnMemory("2.例項化10M的陣列,並建立軟引用");
System.out.println("softRerference.get() : "+softRerference.get());
System.gc();
SoftReferenceTest.printlnMemory("3.記憶體可用容量充足,GC後");
System.out.println("softRerference.get() : "+softRerference.get());
//例項化一個4M的陣列,使記憶體不夠用,並建立軟引用
//free=10M=4M+10M-4M,證明記憶體可用量不足時,GC後byte[10*m]被回收
SoftReference<Object> softRerference2 = new SoftReference<Object>(new byte[4*SoftReferenceTest.M]);
SoftReferenceTest.printlnMemory("4.例項化一個4M的陣列後");
System.out.println("softRerference.get() : "+softRerference.get());
System.out.println("softRerference2.get() : "+softRerference2.get());
}
}
複製程式碼
執行結果:
4.虛引用 PhantomReference
從PhantomReference類的原始碼可以知道,它的get()方法無論何時返回的都只會是null。所以單獨使用虛引用時,沒有什麼意義,需要和引用佇列ReferenceQueue類聯合使用。當執行Java GC時如果一個物件只有虛引用,就會把這個物件加入到與之關聯的ReferenceQueue中。
public class PhantomReferenceTest {
public static int M = 1024*1024;
public static void printlnMemory(String tag){
Runtime runtime = Runtime.getRuntime();
int M = PhantomReferenceTest.M;
System.out.println("\n"+tag+":");
System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
}
public static void main(String[] args) throws InterruptedException {
PhantomReferenceTest.printlnMemory("1.原可用記憶體和總記憶體");
byte[] object = new byte[10*PhantomReferenceTest.M];
PhantomReferenceTest.printlnMemory("2.例項化10M的陣列後");
//建立虛引用
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
PhantomReference<Object> phantomReference = new PhantomReference<Object>(object,referenceQueue);
PhantomReferenceTest.printlnMemory("3.建立虛引用後");
System.out.println("phantomReference : "+phantomReference);
System.out.println("phantomReference.get() : "+phantomReference.get());
System.out.println("referenceQueue.poll() : "+referenceQueue.poll());
//斷開byte[10*PhantomReferenceTest.M]的強引用
object = null;
PhantomReferenceTest.printlnMemory("4.執行object = null;強引用斷開後");
System.gc();
PhantomReferenceTest.printlnMemory("5.GC後");
System.out.println("phantomReference : "+phantomReference);
System.out.println("phantomReference.get() : "+phantomReference.get());
System.out.println("referenceQueue.poll() : "+referenceQueue.poll());
//斷開虛引用
phantomReference = null;
System.gc();
PhantomReferenceTest.printlnMemory("6.斷開虛引用後GC");
System.out.println("phantomReference : "+phantomReference);
System.out.println("referenceQueue.poll() : "+referenceQueue.poll());
}
}
複製程式碼
執行結果:
三、小結
強引用是 Java 的預設引用形式,使用時不需要顯示定義,是我們平時最常使用到的引用方式。不管系統資源有多緊張,Java GC都不會主動回收具有強引用的物件。 弱引用和軟引用一般在引用物件為非必需物件的時候使用。它們的區別是被弱引用關聯的物件在垃圾回收時總是會被回收,被軟引用關聯的物件只有在記憶體不足時才會被回收。 虛引用的get()方法獲取的永遠是null,無法獲取物件例項。Java GC會把虛引用的物件放到引用佇列裡面。可用來在物件被回收時做額外的一些資源清理或事物回滾等處理。 由於無法從虛引獲取到引用物件的例項。它的使用情況比較特別,所以這裡不把虛引用放入表格進行對比。這裡對強引用、弱引用、軟引用進行對比:
引用型別 | GC時JVM記憶體充足 | GC時JVM記憶體不足 |
---|---|---|
強引用 | 不被回收 | 不被回收 |
弱引用 | 被回收 | 被回收 |
軟引用 | 不被回收 | 被回收 |