Java中的四種引用類型之強(qiáng)引用、軟引用、弱引用和虛引用及用法詳解
Java提供了四種不同強(qiáng)度的引用類型,它們直接影響對象的生命周期和垃圾收集行為。理解這些引用類型的區(qū)別對于編寫高效、內(nèi)存友好的Java程序至關(guān)重要。本文將全面剖析這四種引用類型的概念、用法、實(shí)現(xiàn)原理以及實(shí)際應(yīng)用場景。
一、引用類型概述
Java中的引用類型決定了對象與垃圾收集器(GC)的互動方式:
| 引用類型 | 類 | GC行為 | 用途場景 |
|---|---|---|---|
| 強(qiáng)引用 | 默認(rèn) | 永不回收 | 普通對象引用 |
| 軟引用 | SoftReference | 內(nèi)存不足時回收 | 內(nèi)存敏感緩存 |
| 弱引用 | WeakReference | 下次GC時回收 | 規(guī)范化映射、臨時緩存 |
| 虛引用 | PhantomReference | 隨時可能回收 | 對象回收跟蹤、清理操作 |

二、強(qiáng)引用(Strong Reference)
2.1 基本概念
強(qiáng)引用是Java程序中最常見的引用類型,也是默認(rèn)的引用方式。只要強(qiáng)引用存在,對象就永遠(yuǎn)不會被垃圾收集器回收。
Object obj = new Object(); // 強(qiáng)引用
2.2 特點(diǎn)
- 生命周期:只要引用鏈可達(dá),對象就始終存活
- 回收條件:顯式置為null或超出作用域
- 內(nèi)存泄漏:不當(dāng)使用會導(dǎo)致內(nèi)存泄漏
2.3 示例分析
public class StrongReferenceDemo {
public static void main(String[] args) {
Object strongRef = new Object(); // 強(qiáng)引用
// 取消強(qiáng)引用
strongRef = null;
// 此時對象可以被GC回收
System.gc();
}
}2.4 內(nèi)存泄漏場景
public class MemoryLeakDemo {
private static List<Object> list = new ArrayList<>();
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
Object obj = new Object();
list.add(obj); // 靜態(tài)集合持有強(qiáng)引用
obj = null; // 無效操作,因?yàn)閘ist仍持有引用
}
// 即使obj=null,對象仍然無法被回收
}
}三、軟引用(SoftReference)
3.1 基本概念
軟引用描述一些還有用但非必需的對象。只有在內(nèi)存不足時(OOM前),GC才會回收這些對象。
SoftReference<Object> softRef = new SoftReference<>(new Object());
3.2 特點(diǎn)
- 內(nèi)存敏感:在內(nèi)存充足時表現(xiàn)如強(qiáng)引用
- 回收策略:內(nèi)存不足時根據(jù)LRU算法回收
- 使用場景:適合實(shí)現(xiàn)內(nèi)存敏感緩存
3.3 實(shí)現(xiàn)原理
public class SoftReference<T> extends Reference<T> {
// 由JVM維護(hù)的時間戳,記錄最后訪問時間
private long timestamp;
public SoftReference(T referent) {
super(referent);
this.timestamp = clock;
}
// JVM在GC時會調(diào)用此方法
void updateTimestamp() {
this.timestamp = clock;
}
}3.4 緩存示例
public class ImageCache {
private final Map<String, SoftReference<BufferedImage>> cache = new HashMap<>();
public BufferedImage getImage(String path) {
BufferedImage image = null;
// 檢查緩存
SoftReference<BufferedImage> ref = cache.get(path);
if (ref != null) {
image = ref.get();
}
// 緩存未命中
if (image == null) {
image = loadImageFromDisk(path);
cache.put(path, new SoftReference<>(image));
}
return image;
}
private BufferedImage loadImageFromDisk(String path) {
// 實(shí)際實(shí)現(xiàn)從磁盤加載圖像
return null;
}
}3.5 回收測試
public class SoftReferenceDemo {
public static void main(String[] args) {
SoftReference<byte[]> softRef = new SoftReference<>(new byte[1024 * 1024 * 10]); // 10MB
System.out.println("GC前: " + softRef.get());
System.gc();
System.out.println("GC后(內(nèi)存充足): " + softRef.get());
try {
byte[] bigArray = new byte[1024 * 1024 * 100]; // 強(qiáng)制OOM
} catch (OutOfMemoryError e) {
System.out.println("OOM后: " + softRef.get()); // 可能為null
}
}
}四、弱引用(WeakReference)
4.1 基本概念
弱引用描述非必需對象,無論內(nèi)存是否充足,只要發(fā)生GC就會被回收。
WeakReference<Object> weakRef = new WeakReference<>(new Object());
4.2 特點(diǎn)
- 生命周期短:只能存活到下一次GC
- 自動回收:無需手動清除
- 使用場景:規(guī)范化映射、臨時緩存
4.3 WeakHashMap實(shí)現(xiàn)
public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
// 使用弱引用作為Entry的key
private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {
V value;
int hash;
Entry<K,V> next;
Entry(K key, V value, ReferenceQueue<K> queue, int hash, Entry<K,V> next) {
super(key, queue); // key被弱引用持有
this.value = value;
this.hash = hash;
this.next = next;
}
}
}4.4 緩存示例
public class WeakCache<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> ref = cache.get(key);
return ref != null ? ref.get() : null;
}
// 定期清理null值的WeakReference
public void cleanUp() {
cache.entrySet().removeIf(entry ->
entry.getValue() == null || entry.getValue().get() == null);
}
}4.5 回收測試
public class WeakReferenceDemo {
public static void main(String[] args) {
WeakReference<Object> weakRef = new WeakReference<>(new Object());
System.out.println("GC前: " + weakRef.get());
System.gc();
// 注意:這里不能保證GC立即執(zhí)行,可能需要多次調(diào)用或增加內(nèi)存壓力
System.out.println("GC后: " + weakRef.get()); // 很可能為null
}
}五、虛引用(PhantomReference)
5.1 基本概念
虛引用是最弱的引用類型,無法通過它獲取對象實(shí)例,主要用于跟蹤對象被回收的狀態(tài)。
ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
5.2 特點(diǎn)
- 不可達(dá)性:get()始終返回null
- 回收通知:通過ReferenceQueue獲得回收通知
- 使用場景:精細(xì)化的對象回收后處理
5.3 實(shí)現(xiàn)原理
public class PhantomReference<T> extends Reference<T> {
public T get() {
return null; // 始終返回null
}
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}5.4 資源清理示例
public class ResourceCleaner {
private static final ReferenceQueue<Object> queue = new ReferenceQueue<>();
private static final List<CleanupReference> references = new ArrayList<>();
public static void register(Object resource, Runnable cleanupAction) {
references.add(new CleanupReference(resource, cleanupAction, queue));
}
public static void cleanup() {
CleanupReference ref;
while ((ref = (CleanupReference) queue.poll()) != null) {
ref.cleanup();
references.remove(ref);
}
}
private static class CleanupReference extends PhantomReference<Object> {
private final Runnable cleanupAction;
CleanupReference(Object referent, Runnable cleanupAction, ReferenceQueue<? super Object> q) {
super(referent, q);
this.cleanupAction = cleanupAction;
}
void cleanup() {
cleanupAction.run();
}
}
}5.5 使用示例
public class PhantomReferenceDemo {
public static void main(String[] args) {
Object resource = new Object();
// 注冊清理操作
ResourceCleaner.register(resource, () ->
System.out.println("資源被回收,執(zhí)行清理操作"));
// 取消強(qiáng)引用
resource = null;
// 觸發(fā)GC
System.gc();
// 處理清理操作
ResourceCleaner.cleanup();
}
}六、ReferenceQueue的作用
引用隊(duì)列(ReferenceQueue)與軟/弱/虛引用配合使用,主要用途:
- 跟蹤引用狀態(tài):當(dāng)引用對象被回收時,引用本身會被加入隊(duì)列
- 執(zhí)行后續(xù)操作:通過輪詢隊(duì)列執(zhí)行清理工作
- 避免引用堆積:及時清理無用的Reference對象
6.1 使用模式
ReferenceQueue<Object> queue = new ReferenceQueue<>();
WeakReference<Object> ref = new WeakReference<>(new Object(), queue);
// 在另一個線程中處理隊(duì)列
new Thread(() -> {
try {
while (true) {
Reference<?> r = queue.remove();
System.out.println("對象被回收: " + r);
// 執(zhí)行清理操作
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();6.2 各引用類型與隊(duì)列
| 引用類型 | 入隊(duì)時機(jī) | 典型用途 |
|---|---|---|
| 軟引用 | 對象被回收且內(nèi)存不足 | 緩存清理通知 |
| 弱引用 | 對象被回收 | WeakHashMap維護(hù) |
| 虛引用 | 對象被回收 | 資源精確釋放 |
七、四種引用的對比總結(jié)
7.1 特性對比表
| 特性 | 強(qiáng)引用 | 軟引用 | 弱引用 | 虛引用 |
|---|---|---|---|---|
| 回收強(qiáng)度 | 不回收 | 內(nèi)存不足時回收 | 下次GC回收 | 隨時可能回收 |
| get()返回值 | 對象本身 | 對象本身(回收前) | 對象本身(回收前) | 始終null |
| 引用隊(duì)列 | 不支持 | 支持 | 支持 | 必須配合使用 |
| 典型用途 | 普通對象引用 | 內(nèi)存敏感緩存 | 規(guī)范化映射 | 資源清理跟蹤 |
| 實(shí)現(xiàn)類 | - | SoftReference | WeakReference | PhantomReference |
7.2 生命周期圖示

八、實(shí)際應(yīng)用場景
8.1 緩存實(shí)現(xiàn)選擇
強(qiáng)引用緩存:
Map<String, Object> cache = new HashMap<>(); // 可能內(nèi)存泄漏
軟引用緩存:
Map<String, SoftReference<Object>> cache = new HashMap<>(); // 自動釋放
弱引用緩存:
Map<String, WeakReference<Object>> cache = new HashMap<>(); // 短期緩存
8.2 監(jiān)聽器管理
public class ListenerManager {
private final Map<EventListener, WeakReference<Listener>> listeners = new WeakHashMap<>();
public void addListener(EventListener listener) {
listeners.put(listener, new WeakReference<>(listener));
}
// 無需顯式移除,GC會自動清理
}8.3 資源清理最佳實(shí)踐
public class ResourceHolder implements AutoCloseable {
private final Object resource;
private final PhantomReference<Object> phantomRef;
public ResourceHolder(Object resource) {
this.resource = resource;
this.phantomRef = new PhantomReference<>(resource, cleanupQueue);
}
@Override
public void close() {
// 顯式清理
cleanupResource(resource);
}
// 防止忘記調(diào)用close()
protected void finalize() throws Throwable {
if (resource != null) {
cleanupResource(resource);
}
}
private static void cleanupResource(Object resource) {
// 實(shí)際清理邏輯
}
}九、常見問題與解決方案
9.1 內(nèi)存泄漏診斷
問題:即使使用弱引用,內(nèi)存仍在增長
解決方案:
- 檢查是否有強(qiáng)引用意外保留
- 確保正確使用ReferenceQueue清理
- 使用MAT等工具分析內(nèi)存快照
9.2 引用隊(duì)列處理延遲
問題:對象已回收但引用未入隊(duì)
解決方案:
- 確保有活躍線程處理隊(duì)列
- 適當(dāng)調(diào)用System.gc()(僅測試環(huán)境)
- 增加內(nèi)存壓力觸發(fā)GC
9.3 緩存性能優(yōu)化
問題:軟引用緩存頻繁重建
解決方案:
- 調(diào)整JVM內(nèi)存參數(shù)(-Xmx)
- 實(shí)現(xiàn)多級緩存(強(qiáng)引用+軟引用)
- 使用專業(yè)緩存庫(Caffeine, Ehcache)
十、總結(jié)
Java的四種引用類型提供了不同層次的對象生命周期控制:
- 強(qiáng)引用:默認(rèn)選擇,確保對象長期存活
- 軟引用:實(shí)現(xiàn)內(nèi)存敏感緩存,平衡性能與內(nèi)存使用
- 弱引用:避免干預(yù)GC行為,適合規(guī)范化映射
- 虛引用:最精細(xì)的回收跟蹤,用于資源清理
正確運(yùn)用這些引用類型可以:
- 預(yù)防內(nèi)存泄漏
- 優(yōu)化內(nèi)存使用
- 實(shí)現(xiàn)高效的緩存策略
- 精確控制資源生命周期
理解這些引用類型的差異和適用場景,是成為Java高級開發(fā)者的重要一步。在實(shí)際開發(fā)中,應(yīng)根據(jù)具體需求選擇合適的引用類型,并配合ReferenceQueue等機(jī)制實(shí)現(xiàn)健壯的內(nèi)存管理。
到此這篇關(guān)于Java中的四種引用類型之強(qiáng)引用、軟引用、弱引用和虛引用及用法詳解的文章就介紹到這了,更多相關(guān)java 強(qiáng)引用 軟引用 弱引用和虛引用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Centos中yum方式安裝java的實(shí)現(xiàn)示例
這篇文章主要介紹了Centos中yum方式安裝java的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
spring中FactoryBean中的getObject()方法實(shí)例解析
這篇文章主要介紹了spring中FactoryBean中的getObject()方法實(shí)例解析,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下2018-02-02
解決response.setHeader設(shè)置下載文件名無效的問題
這篇文章主要介紹了解決response.setHeader設(shè)置下載文件名無效的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
Java創(chuàng)建和啟動線程的兩種方式實(shí)例分析
這篇文章主要介紹了Java創(chuàng)建和啟動線程的兩種方式,結(jié)合實(shí)例形式分析了java多線程創(chuàng)建、使用相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2019-09-09
Java實(shí)現(xiàn)的微信公眾號獲取微信用戶信息示例
這篇文章主要介紹了Java實(shí)現(xiàn)的微信公眾號獲取微信用戶信息,結(jié)合實(shí)例形式分析了Java微信公眾號獲取微信用戶信息相關(guān)原理、步驟與操作注意事項(xiàng),需要的朋友可以參考下2019-10-10
SpringBoot?SpringSecurity?JWT實(shí)現(xiàn)系統(tǒng)安全策略詳解
Spring?Security是Spring的一個核心項(xiàng)目,它是一個功能強(qiáng)大且高度可定制的認(rèn)證和訪問控制框架。它提供了認(rèn)證和授權(quán)功能以及抵御常見的攻擊,它已經(jīng)成為保護(hù)基于spring的應(yīng)用程序的事實(shí)標(biāo)準(zhǔn)2022-11-11
Spring JDK動態(tài)代理實(shí)現(xiàn)過程詳解
這篇文章主要介紹了Spring JDK動態(tài)代理實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02

