Java中的引用類型
Java中存在四種引用盹沈,它們由強到弱依次是:強引用、軟引用吃谣、弱引用乞封、虛引用。下面我們簡單介紹下除弱引用外的其他三種引用:
- 強引用(Strong Reference):通常我們通過new來創(chuàng)建一個新對象時返回的引用就是一個強引用岗憋,若一個對象通過一系列強引用可到達肃晚,它就是強可達的(strongly reachable),那么它就不被回收
- 弱引用(Weak Reference):弱引用的對象擁有更短暫的生命周期仔戈。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中关串,一旦發(fā)現(xiàn)了只具有弱引用的對象拧廊,不管當前內(nèi)存空間足夠與否,都會回收它的內(nèi)存
- 軟引用(Soft Reference):軟引用和弱引用的區(qū)別在于晋修,若一個對象是弱引用可達吧碾,無論當前內(nèi)存是否充足它都會被回收,而軟引用可達的對象在內(nèi)存不充足時才會被回收飞蚓,因此軟引用要比弱引用“強”一些
- 虛引用(Phantom Reference):虛引用是Java中最弱的引用滤港,那么它弱到什么程度呢?它是如此脆弱以至于我們通過虛引用甚至無法獲取到被引用的對象趴拧,虛引用存在的唯一作用就是當它指向的對象被回收后溅漾,虛引用本身會被加入到引用隊列中,用作記錄它指向的對象已被回收著榴。
判斷弱引用對象的關鍵在于只具有弱引用的對象添履,也就是說,如果一個對象有強引用脑又,那么在系統(tǒng)GC時双炕,是不會回收此對象的,也不會釋放弱引用裙犹。
為什么使用弱引用
Java常通過使用弱引用來避免內(nèi)存泄漏阵谚,例如在JDK中有一種內(nèi)存變量ThreadLocal,通過ThreadLocal變量可以使共享的變量在不同的線程中有不同的副本严卖,原理是在每一個Thread有一個threadLocalMap的屬性席舍,用來存放ThreadLocal對象,ThreadLocalMap中是通過一個Entry[]的散列表存放ThreadLocal變量以及ThreadLocal的value哮笆,而作為Entry的key的ThreadLocal就是使用的弱引用来颤,結(jié)構如下:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry通過繼承了WeakReference并通過get、set設置ThreadLocal為Entry的referent稠肘。
這里為什么要使用弱引用呢福铅?
原因是如果不使用弱引用,那么當持有value的強引用釋放掉后项阴,當線程沒有回收釋放時滑黔,threadLocalMap會一直持有ThreadLocal以及value的強應用,導致value不能夠被回收环揽,從而造成內(nèi)存泄漏拷沸。
通過使用弱引用,當ThreadLocal的強引用釋放掉后薯演,通過一次系統(tǒng)gc檢查撞芍,發(fā)現(xiàn)ThreadLocal對象只有threadLocalMap中Entry的若引用持有,此時根據(jù)弱引用的機制就會回收ThreadLocal對象跨扮,從而避免了內(nèi)存泄露序无。當然ThreadLocal還有一些額外的保護措施验毡,詳細分析可以參考:死磕Java源碼之ThreadLocal實現(xiàn)分析
這里我們可以通過一個示例來驗證一下:
WeakReferenceDemo.java
import java.lang.ref.WeakReference;
/**
* 弱引用回收測試
*/
public class WeakReferenceDemo {
public static WeakReference<String> weakReference1;
public static WeakReference<String> weakReference2;
public static void main(String[] args) {
test1();
//可以輸出hello值,此時兩個弱引用扔持有對象帝嗡,而且未進行gc
System.out.println("未進行gc時晶通,只有弱引用指向value內(nèi)存區(qū)域:" + weakReference1.get());
//此時已無強一用執(zhí)行"value"所在內(nèi)存區(qū)域,gc時會回收弱引用
System.gc();
//此時輸出都為nuill
System.out.println("進行gc時哟玷,只有弱引用指向value內(nèi)存區(qū)域:" + weakReference1.get());
}
public static void test1() {
String hello = new String("value");
weakReference1 = new WeakReference<>(hello);
System.gc();
//此時gc不會回收弱引用狮辽,因為字符串"value"仍然被hello對象強引用
System.out.println("進行gc時,強引用與弱引用同時指向value內(nèi)存區(qū)域:" + weakReference1.get());
}
}
輸出:
進行gc時巢寡,強引用與弱引用同時指向value內(nèi)存區(qū)域:value
未進行gc時喉脖,只有弱引用指向value內(nèi)存區(qū)域:value
進行gc時,只有弱引用指向value內(nèi)存區(qū)域:null
分析輸出結(jié)果可以看出:
當有強引用指向value內(nèi)存區(qū)域時抑月,即使進行gc树叽,弱引用也不會被釋放,對象不回被回收谦絮。
當無強引用指向value內(nèi)存區(qū)域是题诵,此時進行gc,弱引用會被釋放层皱,對象將會執(zhí)行回收流程性锭。
引用隊列
下面我們來簡單地介紹下引用隊列的概念。實際上叫胖,WeakReference類有兩個構造函數(shù):
//創(chuàng)建一個指向給定對象的弱引用``WeakReference(T referent)
//創(chuàng)建一個指向給定對象并且登記到給定引用隊列的弱引用``WeakReference(T referent, ReferenceQueue<? ``super` `T> q)
我們可以看到第二個構造方法中提供了一個ReferenceQueue類型的參數(shù)草冈,通過提供這個參數(shù),我們便把創(chuàng)建的弱引用對象注冊到了一個引用隊列上臭家,這樣當它被垃圾回收器清除時疲陕,就會把它送入這個引用隊列中方淤,我們便可以對這些被清除的弱引用對象進行統(tǒng)一管理钉赁。
參考:
http://www.reibang.com/p/640f2c0ac4b0
https://www.cnblogs.com/fengbs/p/7019687.html
http://www.importnew.com/21206.html