我的原則:先會用再說,內部慢慢來。
學以致用,根據場景學源碼
一、概念與區別
- Java中4種引用的強度由高到低依次爲:強引用 -> 軟引用 -> 弱引用 -> 虛引用
- 用途與區別:
引用類型 | 被垃圾回收時機 | 用途 | 生存時間 |
---|---|---|---|
強引用 | 從來不會 | 對象的一般狀態 | JVM停止運行時終止 |
軟引用 | 當內存不足前 | 對象緩存 | 內存不足時終止 |
弱引用 | GC一看到立刻回收 | 對象緩存 | 垃圾回收後終止 |
虛引用 | 隨時隨刻 | 跟蹤對象的垃圾回收 | 垃圾回收後終止 |
二、架構
三、代碼demo
3.1 強引用 Strong Reference
- 生命週期:JVM 寧願拋出OutOfMemoryError錯誤。
代碼demo:
Object strongReference = new Object();
public void hello() {
Object strongReference = new Object();
// ...
}
GC回收時機:
- strongReference = null,那麼無引用指向該 new Object()。
- 方法結束,java方法棧中局部變量被回收。
否則:JVM 寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會GC該對象來解決內存不足的問題。
3.2 軟引用 (SoftReference)
- 生命週期:JVM 拋出OutOfMemoryError錯誤之前,回收這個軟引用指向的對象堆內佔用的空間。
目的:儘可能保留該對象,但是萬不得已沒內存的時候,可以進行回收
GC回收時機:
- strongReference = null,那麼無引用指向該 new Object()。
- 方法結束,java方法棧中局部變量被回收。
- 堆內存不足,準備拋出OutOfMemoryError錯誤之前,處理一波軟引用。
注意:軟引用就算你調用 System.gc() 在內存充足的情況下,也不會回收這個對象的。
場景:瀏覽器的後退按鈕。(緩存數據能保證加載快,但是內存不足的情況下,被回收了,重新去服務器加載也是可以接受的)
代碼demo:
// 獲取瀏覽器對象進行瀏覽
Browser browser = new Browser();
// 從後臺程序加載瀏覽頁面
BrowserPage page = browser.getPage();
// 將瀏覽完畢的頁面置爲軟引用
SoftReference softReference = new SoftReference(page);
// 回退或者再次瀏覽此頁面時
if(softReference.get() != null) {
// 內存充足,還沒有被回收器回收,直接獲取緩存
page = softReference.get();
} else {
// 內存不足,軟引用的對象已經回收
page = browser.getPage();
// 重新構建軟引用
softReference = new SoftReference(page);
}
3.3 弱引用 (WeakReference)
- 生命週期:GC一看到,立馬消滅(首次看到標記,下次gc回收)
- 使用場景:
- 一個對象是偶爾(很少)的使用
- 一定程度上可以避免內存泄漏(詳看 ThreadLocal類)
- ThreadLocal.ThreadLocalMap.Entry
public class ThreadLocal<T> {
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
}
- Thread
public java.lang.class Thread implements Runnable {
...
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
...
}
4. 虛引用(PhantomReference)
- 生命週期:任何時候都可能被GC回收 (如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收)
代碼demo:
public class _03_00_TestPhamtonReference {
public static void main(String[] args) {
ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
PhantomReference<String> phantomReference = new PhantomReference<>("hello",referenceQueue);
System.out.println(phantomReference.get());
}
}
輸出:
null
結論:
剛一出來就被消滅