Java引用總結--StrongReference、SoftReference、WeakReference、PhantomReference

1 Java引用介紹

   Java從1.2版本開始引入了4種引用,這4種引用的級別由高到低依次爲:

   強引用  >  軟引用  >  弱引用  >  虛引用

⑴強引用(StrongReference)
    強引用是使用最普遍的引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足的問題。

⑵軟引用(SoftReference)

    如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高速緩存。

    軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。

⑶弱引用(WeakReference)

    弱引用與軟引用的區別在於:只具有弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由於垃圾回收器是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。

    弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。

⑷虛引用(PhantomReference)

    “虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定對象的生命週期。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。

    虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之 關聯的引用隊列中。

 

由於引用和內存回收關係緊密。下面,先通過實例對內存回收有個認識;然後,進一步通過引用實例加深對引用的瞭解。

 

 

 


2 內存回收

創建公共類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(),否則它不會進行垃圾回收;在內存不足的情況下,垃圾回收將自動運行


3、Java對引用的分類

級別

什麼時候被垃圾回收

用途

生存時間

強引用

從來不會

對象的一般狀態

JVM停止運行時終止

軟引用

在內存不足時

對象簡單?緩存

內存不足時終止

弱引用

在垃圾回收時

對象緩存

gc運行後終止

虛引用

Unknown

Unknown

Unknown

 


package test05;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

import org.junit.Test;
/**
 * 1、有輸出:代表垃圾回收機制執行時,對象被回收
 * 2、垃圾回收機制的優先級別較低,一般不會自動執行
 * 3、在內存耗盡時:ReferenceTest.drainMemory();,垃圾回收機制會自動執行
 * 4、System.gc(); //顯式調用垃圾回收機制,也可讓垃圾回收機制執行
 * @author Do Not Cry
 *
 */
public class NoGarbageRetrieve {
	//一、StrongReference
	//(一)、強引用  對象依舊被引用:
	@Test
	public void test04() {
		MyDate date = new MyDate();
		//ReferenceTest.drainMemory();
		System.gc();
	}
	//沒有輸出:強引用對象依舊被引用,即使垃圾回收機制顯式執行,也不會回收強引用對象
		
	
	//(二)、強引用  對象沒有被引用:
	//1、沒有耗盡內存,也沒有顯式調用垃圾回收機制
	@Test
    public void test01() {
        MyDate date = new MyDate();
        date = null;
        
    }
    //沒有輸出:說明JVM沒有執行垃圾回收操作
    //即:強引用的對象,即使不再被引用(date = null),也不會被垃圾回收機制立即回收
	
	//2、耗盡內存,讓垃圾回收機制自動執行
	@Test
    public void test02() {
        MyDate date = new MyDate();
        date = null;
        ReferenceTest.drainMemory();
    }
	//有輸出:內存耗盡,垃圾回收機制自動執行,沒有被引用的強引用對象被回收
	
	//3、顯示調用垃圾回收機制
	@Test
    public void test03() {
        MyDate date = new MyDate();
        date = null;
        System.gc();
    }
    //有輸出:垃圾回收機制顯式執行,沒有被引用的強引用對象被回收
	
	
	//二、SoftReference
	//1、軟引用對象仍被引用
	@Test
	public void teset05() {
		SoftReference ref = new SoftReference(new MyDate());
		//ReferenceTest.drainMemory(); //即使內存耗盡,垃圾回收機制也不會受仍被引用的軟引用對象
		System.gc();
	}
	//沒有輸出:來及回收機制顯式執行,軟引用對象沒有被回收
	
	//2、軟引用對象沒有被引用
	@Test
	public void test06() {
		SoftReference ref = new SoftReference(new MyDate());
		ref = null;
		System.gc();
	}
	//有輸出:軟引用對象沒有被引用,垃圾回收機制執行時,對象被回收
	
	//三、WeakReference
	//1、弱引用對象仍被引用
	@Test
	public void test07() {
		 WeakReference ref = new WeakReference(new MyDate());
		 //ReferenceTest.drainMemory(); //內存耗盡時,會回收軟引用對象,機制該對象仍被引用
	     System.gc(); 
	}
	//有輸出:即使弱引用仍被引用,在JVM垃圾回收運行時,弱引用被終止.
	
	//2、弱引用對象沒有被引用
	@Test
	public void test08() {
		 WeakReference ref = new WeakReference(new MyDate());
		 ref = null;
	     System.gc(); 
	}
	//有輸出:弱引用沒被引用,在JVM垃圾回收運行時,弱引用被終止.
	
	
	//四、PhantomReference  --垃圾回收情況(同弱引用對象一樣)
	//1、虛引用對象仍被引用
	@Test
	public void test09() {
		ReferenceQueue queue = new ReferenceQueue();
        PhantomReference ref = new PhantomReference(new MyDate(), queue);
        System.gc();
	}
	//有輸出:
	
	//2、虛引用對象沒有被引用
	@Test
	public void teset10() {
		ReferenceQueue queue = new ReferenceQueue();
        PhantomReference ref = new PhantomReference(new MyDate(), queue);
        ref = null;
        System.gc();
	}
	//有輸出:
}


發佈了39 篇原創文章 · 獲贊 52 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章