java引用類型WeakReference用法及原理詳解
1. 核心概念:什么是 WeakReference?
WeakReference(弱引用)是 Java java.lang.ref 包下提供的一種引用類型。它的特點(diǎn)是:當(dāng)垃圾回收器(GC)進(jìn)行掃描時(shí),無(wú)論當(dāng)前內(nèi)存是否充足,只要被弱引用關(guān)聯(lián)的對(duì)象沒有其他強(qiáng)引用**,那么這個(gè)對(duì)象就會(huì)被回收。
這與我們平時(shí)普通的對(duì)象引用(強(qiáng)引用,Strong Reference)形成鮮明對(duì)比:
- 強(qiáng)引用:
Object obj = new Object();。只要強(qiáng)引用存在,垃圾收集器就永遠(yuǎn)不會(huì)回收被引用的對(duì)象。 - 弱引用:
WeakReference<Object> weakRef = new WeakReference<>(obj);。即使弱引用存在,只要發(fā)生 GC 且對(duì)象沒有強(qiáng)引用,它就會(huì)被回收。
2. 工作原理
WeakReference 的工作原理與 Java 的垃圾回收機(jī)制緊密相關(guān)。JVM 中的垃圾回收器使用可達(dá)性分析算法來判斷對(duì)象是否存活。
可達(dá)性分析:
GC Roots 是所有引用鏈的起點(diǎn)(如虛擬機(jī)棧中的局部變量、靜態(tài)變量等)。如果一個(gè)對(duì)象到 GC Roots 沒有任何引用鏈相連,則證明此對(duì)象是不可用的。
引用級(jí)別(強(qiáng)度從高到低):
- 強(qiáng)引用 (Strong Reference):絕不回收。
- 軟引用 (Soft Reference):內(nèi)存不足時(shí)(OOM 前)才回收。
- 弱引用 (Weak Reference):發(fā)現(xiàn)即回收。只要 GC 發(fā)現(xiàn)一個(gè)對(duì)象只被弱引用指向,就會(huì)立刻將其標(biāo)記為可回收對(duì)象,并在下一次 GC 時(shí)回收其內(nèi)存。
- 虛引用 (Phantom Reference):對(duì)象回收跟蹤。無(wú)法通過它獲取對(duì)象實(shí)例,其唯一目的是在對(duì)象被回收時(shí)收到一個(gè)系統(tǒng)通知。
WeakReference 的工作流程可以簡(jiǎn)化為:
- 你創(chuàng)建一個(gè)對(duì)象,并用一個(gè)
WeakReference來包裝它。 - 當(dāng)你將對(duì)象的強(qiáng)引用置為
null(obj = null;)后,這個(gè)對(duì)象就只剩下WeakReference這一個(gè)“微弱”的引用了。 - 下一次垃圾回收發(fā)生時(shí),GC 會(huì)發(fā)現(xiàn)這個(gè)對(duì)象是“弱可達(dá)”的。
- GC 會(huì)立即回收這個(gè)對(duì)象的內(nèi)存。
- 回收之后,你的
WeakReference.get()方法將返回null。
3. 主要用法和示例
基本使用
import java.lang.ref.WeakReference;
public class WeakReferenceDemo {
public static void main(String[] args) {
// 1. 創(chuàng)建一個(gè)強(qiáng)引用對(duì)象
Object strongRef = new Object();
// 2. 用弱引用包裝這個(gè)對(duì)象
WeakReference<Object> weakRef = new WeakReference<>(strongRef);
// 3. 此時(shí)既有強(qiáng)引用,也有弱引用。GC 不會(huì)回收它。
System.out.println("Before GC (Strong ref exists): " + weakRef.get()); // 有輸出
// 4. 切斷強(qiáng)引用!這是關(guān)鍵一步。
strongRef = null;
// 5. 建議系統(tǒng)進(jìn)行垃圾回收(這只是建議,不保證立即執(zhí)行)
System.gc();
// 6. 給 GC 一點(diǎn)時(shí)間執(zhí)行
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 7. 檢查弱引用指向的對(duì)象是否被回收
System.out.println("After GC (Only weak ref): " + weakRef.get()); // 輸出 null
}
}
輸出結(jié)果:
Before GC (Strong ref exists): java.lang.Object@1b6d3586
After GC (Only weak ref): null
典型應(yīng)用場(chǎng)景:實(shí)現(xiàn)內(nèi)存敏感的緩存
這是 WeakReference 最經(jīng)典的使用場(chǎng)景。你可以用它來構(gòu)建一個(gè)緩存,當(dāng)內(nèi)存不足時(shí),緩存中的條目會(huì)被自動(dòng)清除,從而避免內(nèi)存泄漏(Memory Leak)。
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class SimpleCache<K, V> {
private final Map<K, WeakReference<V>> cache = new HashMap<>();
public void put(K key, V value) {
cache.put(key, new WeakReference<>(value));
}
public V get(K key) {
WeakReference<V> weakRef = cache.get(key);
if (weakRef != null) {
// 如果弱引用指向的對(duì)象還沒被GC回收,就返回它
return weakRef.get();
}
// 如果被回收了或者不存在,就返回null
return null;
}
// 可以添加一個(gè)方法來清理Map中那些已經(jīng)被GC回收的條目(key還在,value為null的Entry)
public void cleanUp() {
cache.entrySet().removeIf(entry -> entry.getValue().get() == null);
}
}
為什么這樣用?
緩存的數(shù)據(jù)通常不是至關(guān)重要的。如果內(nèi)存緊張,丟棄部分緩存數(shù)據(jù)是可以接受的,應(yīng)用程序可以從原始數(shù)據(jù)源(如數(shù)據(jù)庫(kù))重新加載。使用 WeakReference 可以讓 GC 充當(dāng)你的“緩存清理工”,自動(dòng)管理內(nèi)存,你無(wú)需編寫復(fù)雜的內(nèi)存淘汰算法(如 LRU)。
注意: 上面的 SimpleCache 中的 Map 本身仍然持有 key 的強(qiáng)引用和 WeakReference 對(duì)象的強(qiáng)引用。如果 key 本身是一個(gè)大對(duì)象,你可能需要類似 WeakHashMap 的結(jié)構(gòu)。
4. 注意事項(xiàng)
配合 ReferenceQueue 使用:你可以將一個(gè)
ReferenceQueue傳遞給WeakReference的構(gòu)造函數(shù)。當(dāng)弱引用指向的對(duì)象被垃圾回收后,這個(gè)WeakReference對(duì)象本身會(huì)被加入到這個(gè)隊(duì)列中。這允許你執(zhí)行一些后續(xù)的清理工作,例如從緩存Map中移除對(duì)應(yīng)的鍵。ReferenceQueue queue = new ReferenceQueue<>();
WeakReference weakRef = new WeakReference<>(new Object(), queue);
// … 之后可以檢查 queue.poll() 來獲取被回收的引用不是銀彈:
WeakReference主要用于緩解內(nèi)存問題,而不是解決邏輯問題。你的代碼必須能處理get()返回null的情況(即對(duì)象已被回收)。性能:頻繁創(chuàng)建
WeakReference對(duì)象和進(jìn)行 GC 本身也有開銷,不要濫用。與 SoftReference 的區(qū)別:
WeakReference:GC 看到就回收,更激進(jìn)。SoftReference:只有在內(nèi)存不足(即將發(fā)生 OOM)時(shí)才會(huì)回收,更適合做緩存,因?yàn)槟鼙M可能長(zhǎng)時(shí)間地保留數(shù)據(jù)。而WeakReference的緩存被回收得更快。
總結(jié)
| 特性 | 描述 |
|---|---|
| 本質(zhì) | 一種不影響垃圾回收決策的引用類型。 |
| 核心原則 | 僅被弱引用關(guān)聯(lián)的對(duì)象,在 GC 時(shí)必定被回收。 |
| 主要用途 | 實(shí)現(xiàn)規(guī)范化映射(如 WeakHashMap)、內(nèi)存敏感的高速緩存、監(jiān)聽器列表等,防止因緩存等原因?qū)е碌膬?nèi)存泄漏。 |
| 如何使用 | 1. 創(chuàng)建 WeakReference 包裝對(duì)象。2. 確保沒有其他強(qiáng)引用指向該對(duì)象。 3. 在代碼中總是檢查 get() 是否返回 null。 |
到此這篇關(guān)于java引用類型WeakReference用法及原理的文章就介紹到這了,更多相關(guān)java WeakReference用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中File與MultipartFile互轉(zhuǎn)代碼示例
在Java開發(fā)中,當(dāng)需要將本地File對(duì)象轉(zhuǎn)換為MultipartFile對(duì)象以處理文件上傳時(shí),可以通過實(shí)現(xiàn)MultipartFile接口或使用CommonsMultipartFile類來實(shí)現(xiàn),本文提供了詳細(xì)的轉(zhuǎn)換方法和代碼示例,需要的朋友可以參考下2024-10-10
Spring Validation的校驗(yàn)順序問題及解決過程
本文主要介紹了使用@GroupSequence注解來解決接口入?yún)⑿r?yàn)順序不穩(wěn)定的問題,并提供了具體的實(shí)現(xiàn)步驟和代碼示例2026-01-01
5分鐘搭建SpringCloud Eureka服務(wù)注冊(cè)中心的實(shí)現(xiàn)
這篇文章主要介紹了5分鐘搭建SpringCloud Eureka服務(wù)注冊(cè)中心的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Spring?Boot:Idea從零開始初始化后臺(tái)項(xiàng)目的教程
這篇文章主要介紹了Spring?Boot:Idea從零開始初始化后臺(tái)項(xiàng)目的教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
springboot如何獲取接口下所有實(shí)現(xiàn)類
這篇文章主要介紹了springboot如何獲取接口下所有實(shí)現(xiàn)類問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
利用Sharding-Jdbc組件實(shí)現(xiàn)分表
這篇文章主要為大家詳細(xì)介紹了利用Sharding-Jdbc組件實(shí)現(xiàn)分表,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07

