強引用
正常的引用,生命周期最長,例如 Object obj = new Object(); 當JVM內存不足時,寧可拋出OutOfMemoryError,也不願回收存活着強引用的對象。
對象還活着嗎?當一個普通對象沒有其他引用關系,只要超過了引用的作用域或者顯示的將引用賦值為null時,你的對象就表明不是存活着,這樣就會可以被GC回收了。
軟引用
生命周期比強引用短,通過SoftReference類實現,可以通過get方法獲取對象。
A
當JVM內存不足時會先回收軟引用指向的對象,即拋出OutOfMemoryError之前,會去清理軟引用對象。
軟引用通常會在最后一次引用后,還能保持一段時間,默認值是根據堆剩余空間計算的。
如果軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用 隊列中。軟引用可以和引用隊列(referenceQueue)聯用,我們可以調用ReferenceQueue的poll()方法來檢查是否有它所關心的對象被回收。如果隊列為空,將返回一個null,否則該方法返回隊列中前面的一個Reference對象。
應用場景:軟引用一般用來實現內存敏感的緩存,如果有空閑內存就可以保留緩存,當內存不足時就清理掉,這樣就保證使用緩存的同時不會耗盡內存,如圖片緩存框架中緩存圖片就是通過軟引用實現。
弱引用
弱引用是通過WeakReference類實現的,它的生命周期比軟引用還要短,也是通過get()方法獲取對象。
JVM內存回收時,不論是否內存不足,都會回收弱引用的對象。由於垃圾回收器是一個優先級很低的線程,因此不一定會很快回收弱引用的對象。
弱引用可以配合引用隊列,如果弱引用所引用的對象被垃圾 回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。
應用場景:弱引用適用於內存敏感的緩存,如ThreadLocal中的key就用到了弱引用。
幻象引用
通過PhantomReference類實現,任何時候都可以被回收,可以看作沒有引用。必須和引用隊列聯用。無法通過get方法獲取對象。
幻象引用僅僅是提供了一種確保對象被 fnalize 以后,做某些事情的機制。當垃圾回收器准備回收一個對象時,如 果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。如果程序發現某個虛引用已經被加入到引用隊列,那么就可以在所引用的對象的內存被回收之 前采取一些程序行動。
應用場景:可用來跟蹤對象被垃圾回收器回收的活動,當一個虛引用關聯的對象被垃圾收集器回收之前會收到一條系統通知。
Object counter = new Object();
ReferenceQueue refQueue = new ReferenceQueue<>();
PhantomReference<Object> p = new PhantomReference<>(counter, refQueue);
counter = null;
Sysem.gc();
try {
// Remove是一個阻塞方法,可以指定timeout,或者選擇一直阻塞
Reference<Object> ref = refQueue.remove(1000L);
if (ref != null) {
// do something
}
} catch (InterruptedException e) {
// Handle it
}
Reachability Fence
按照Java語言規范,如果一個對象沒有指向強引用,就符合垃圾收集的標准。但有的場景中,對象本身並沒有強引用,但是也許它的部分屬性還在被使用。為了防止對象被回收,我們需要一個方法,在沒有強引用情況下,通知JVM對象是在被使用的。
Java9之后,java.lang.ref.Reference給我們提供了一種方法, reachabilityFence,來聲明某些沒有強引用的對象,依然強可達。編程時,可以將需要reachability保障的代碼段利用try-fnally包圍起來,在finally里明確聲明對象強可達。
class Resource {
private satic ExternalResource[] externalResourceArray = ...
int myIndex; Resource(...) {
myIndex = ...
externalResourceArray[myIndex] = ...;
...
}
protected void fnalize() {
externalResourceArray[myIndex] = null;
...
}
public void action() {
try {
// 需要被保護的代碼
int i = myIndex;
Resource.update(externalResourceArray[i]);
} fnally {
// 調用reachbilityFence,明確保障對象srongly reachable
Reference.reachabilityFence(this);
}
}
private static void update(ExternalResource ext) {
ext.status = ...;
}
}
//非強引用
new Resource().action()
在JDK源碼中,reachabilityFence大多使用在Executors或者類似新的HTTP/2客戶端代碼中,大部分都是異步調用的情況。
參考
《深入理解Java虛擬機》周志明
《Java核心技術36講》楊曉峰
https://baijiahao.baidu.com/s?id=1629253892215446066&wfr=spider&for=pc