(轉)Java弱引用(WeakReference)的理解與使用

轉: https://www.cnblogs.com/zjj1996/p/9140385.html

看到篇帖子, 國外一個技術面試官在面試senior java developer的時候, 問到一個weak reference相關的問題. 他沒有期望有人能夠完整解釋清楚weak reference是什麼, 怎麼用, 只是期望有人能夠提到這個concept和java的GC相關. 很可惜的是, 20多個擁有5年以上java開發經驗的面試者中, 只有兩人知道weak reference的存在, 而其中只有一人實際用到過他. 無疑, 在interviewer眼中, 對於weak reference的理解和應用在面試中給了這一個interviewee相當多的加分.  所以, 將我對於這個技術的理解和使用總結在這篇博客裏, 希望讀者和自己通過讀和寫這篇帖子, 能夠在以後的工作和麪試中獲得加分.

在Java裏, 當一個對象o被創建時, 它被放在Heap裏. 當GC運行的時候, 如果發現沒有任何引用指向o, o就會被回收以騰出內存空間. 或者換句話說, 一個對象被回收, 必須滿足兩個條件: 1)沒有任何引用指向它 2)GC被運行.

在現實情況寫代碼的時候, 我們往往通過把所有指向某個對象的referece置空來保證這個對象在下次GC運行的時候被回收 (可以用java -verbose:gc來觀察gc的行爲)

Object c = new Car();
c=null;

但是, 手動置空對象對於程序員來說, 是一件繁瑣且違背自動回收的理念的.  對於簡單的情況, 手動置空是不需要程序員來做的, 因爲在java中, 對於簡單對象, 當調用它的方法執行完畢後, 指向它的引用會被從stack中popup, 所以他就能在下一次GC執行時被回收了.

但是, 也有特殊例外. 當使用cache的時候, 由於cache的對象正是程序運行需要的, 那麼只要程序正在運行, cache中的引用就不會被GC給(或者說, cache中的reference擁有了和主程序一樣的life cycle). 那麼隨着cache中的reference越來越多, GC無法回收的object也越來越多, 無法被自動回收. 當這些object需要被回收時, 回收這些object的任務只有交給程序編寫者了. 然而這卻違背了GC的本質(自動回收可以回收的objects).

所以, java中引入了weak reference. 相對於前面舉例中的strong reference:

Object c = new Car(); //只要c還指向car object, car object就不會被回收

 當一個對象僅僅被weak reference指向, 而沒有任何其他strong reference指向的時候, 如果GC運行, 那麼這個對象就會被回收. weak reference的語法是:

WeakReference<Car> weakCar = new WeakReference(Car)(car);

 當要獲得weak reference引用的object時, 首先需要判斷它是否已經被回收:

weakCar.get();

 如果此方法爲空, 那麼說明weakCar指向的對象已經被回收了.

下面來看一個例子:

package weakreference;
/**
 * @author wison
 */
public class Car {
	private double price;
	private String colour;
	
	public Car(double price, String colour){
		this.price = price;
		this.colour = colour;
	}
	
	public double getPrice() {
		return price;
	}
	public void setPrice(double price) {
		this.price = price;
	}
	public String getColour() {
		return colour;
	}
	public void setColour(String colour) {
		this.colour = colour;
	}
	
	public String toString(){
		return colour +"car costs $"+price;
	}
	
}
package weakreference;

import java.lang.ref.WeakReference;

/**
 * @author wison
 */
public class TestWeakReference {

	
	public static void main(String[] args) {
		
		Car car = new Car(22000,"silver");
		WeakReference<Car> weakCar = new WeakReference<Car>(car);
		int i=0;
		while(true){
			if(weakCar.get()!=null){
				i++;
				System.out.println("Object is alive for "+i+" loops - "+weakCar);
			}else{
				System.out.println("Object has been collected.");
				break;
			}
		}
	}

}

在上例中, 程序運行一段時間後, 程序打印出"Object has been collected." 說明, weak reference指向的對象的被回收了.

值得注意的一點 , 即使有 car 引用指向對象, 且 car 是一個strong reference, weak reference weakCar指向的對象仍然被回收了. 這是因爲java的編譯器在發現進入while循環之後, car 已經沒有被使用了, 所以進行了優化(將其置空?). 當把TestWeakReference.java修改爲:

package weakreference;

import java.lang.ref.WeakReference;

/**
 * @author wison
 */
public class TestWeakReference {

	public static void main(String[] args) {
		
		Car car = new Car(22000,"silver");
		WeakReference<Car> weakCar = new WeakReference<Car>(car);
		
		int i=0;
		
		while(true){
			System.out.println("here is the strong reference 'car' "+car);
			if(weakCar.get()!=null){
				i++;
				System.out.println("Object is alive for "+i+" loops - "+weakCar);
			}else{
				System.out.println("Object has been collected.");
				break;
			}
		}
	}

}

 weak reference指向的object就不會被回收了. 因爲還有一個strong reference car 指向它.

* WeakReference的一個特點是它何時被回收是不可確定的, 因爲這是由GC運行的不確定性所確定的. 所以, 一般用weak reference引用的對象是有價值被cache, 而且很容易被重新被構建, 且很消耗內存的對象.

ReferenceQueue

在weak reference指向的對象被回收後, weak reference本身其實也就沒有用了. java提供了一個ReferenceQueue來保存這些所指向的對象已經被回收的reference. 用法是在定義WeakReference的時候將一個ReferenceQueue的對象作爲參數傳入構造函數.

其他類型的references

-SoftReference

soft reference和weak reference一樣, 但被GC回收的時候需要多一個條件: 當系統內存不足時(GC是如何判定系統內存不足? 是否有參數可以配置這個threshold?), soft reference指向的object纔會被回收. 正因爲有這個特性, soft reference比weak reference更加適合做cache objects的reference. 因爲它可以儘可能的retain cached objects, 減少重建他們所需的時間和消耗.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章