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

二、強(qiáng)引用(Strong Reference)
2.1 基本概念
強(qiáng)引用是Java程序中最常見的引用類型,也是默認(rèn)的引用方式。只要強(qiáng)引用存在,對(duì)象就永遠(yuǎn)不會(huì)被垃圾收集器回收。
Object obj = new Object(); // 強(qiáng)引用
2.2 特點(diǎn)
- 生命周期:只要引用鏈可達(dá),對(duì)象就始終存活
- 回收條件:顯式置為null或超出作用域
- 內(nèi)存泄漏:不當(dāng)使用會(huì)導(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;
// 此時(shí)對(duì)象可以被GC回收
System.gc();
}
}2.4 內(nèi)存泄漏場(chǎng)景
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; // 無(wú)效操作,因?yàn)閘ist仍持有引用
}
// 即使obj=null,對(duì)象仍然無(wú)法被回收
}
}三、軟引用(SoftReference)
3.1 基本概念
軟引用描述一些還有用但非必需的對(duì)象。只有在內(nèi)存不足時(shí)(OOM前),GC才會(huì)回收這些對(duì)象。
SoftReference<Object> softRef = new SoftReference<>(new Object());
3.2 特點(diǎn)
- 內(nèi)存敏感:在內(nèi)存充足時(shí)表現(xiàn)如強(qiáng)引用
- 回收策略:內(nèi)存不足時(shí)根據(jù)LRU算法回收
- 使用場(chǎng)景:適合實(shí)現(xiàn)內(nèi)存敏感緩存
3.3 實(shí)現(xiàn)原理
public class SoftReference<T> extends Reference<T> {
// 由JVM維護(hù)的時(shí)間戳,記錄最后訪問時(shí)間
private long timestamp;
public SoftReference(T referent) {
super(referent);
this.timestamp = clock;
}
// JVM在GC時(shí)會(huì)調(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 回收測(cè)試
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 基本概念
弱引用描述非必需對(duì)象,無(wú)論內(nèi)存是否充足,只要發(fā)生GC就會(huì)被回收。
WeakReference<Object> weakRef = new WeakReference<>(new Object());
4.2 特點(diǎn)
- 生命周期短:只能存活到下一次GC
- 自動(dòng)回收:無(wú)需手動(dòng)清除
- 使用場(chǎng)景:規(guī)范化映射、臨時(shí)緩存
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 回收測(cè)試
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 基本概念
虛引用是最弱的引用類型,無(wú)法通過它獲取對(duì)象實(shí)例,主要用于跟蹤對(duì)象被回收的狀態(tài)。
ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
5.2 特點(diǎn)
- 不可達(dá)性:get()始終返回null
- 回收通知:通過ReferenceQueue獲得回收通知
- 使用場(chǎng)景:精細(xì)化的對(duì)象回收后處理
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();
// 注冊(cè)清理操作
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ì)象被回收時(shí),引用本身會(huì)被加入隊(duì)列
- 執(zhí)行后續(xù)操作:通過輪詢隊(duì)列執(zhí)行清理工作
- 避免引用堆積:及時(shí)清理無(wú)用的Reference對(duì)象
6.1 使用模式
ReferenceQueue<Object> queue = new ReferenceQueue<>();
WeakReference<Object> ref = new WeakReference<>(new Object(), queue);
// 在另一個(gè)線程中處理隊(duì)列
new Thread(() -> {
try {
while (true) {
Reference<?> r = queue.remove();
System.out.println("對(duì)象被回收: " + r);
// 執(zhí)行清理操作
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();6.2 各引用類型與隊(duì)列
| 引用類型 | 入隊(duì)時(shí)機(jī) | 典型用途 |
|---|---|---|
| 軟引用 | 對(duì)象被回收且內(nèi)存不足 | 緩存清理通知 |
| 弱引用 | 對(duì)象被回收 | WeakHashMap維護(hù) |
| 虛引用 | 對(duì)象被回收 | 資源精確釋放 |
七、四種引用的對(duì)比總結(jié)
7.1 特性對(duì)比表
| 特性 | 強(qiáng)引用 | 軟引用 | 弱引用 | 虛引用 |
|---|---|---|---|---|
| 回收強(qiáng)度 | 不回收 | 內(nèi)存不足時(shí)回收 | 下次GC回收 | 隨時(shí)可能回收 |
| get()返回值 | 對(duì)象本身 | 對(duì)象本身(回收前) | 對(duì)象本身(回收前) | 始終null |
| 引用隊(duì)列 | 不支持 | 支持 | 支持 | 必須配合使用 |
| 典型用途 | 普通對(duì)象引用 | 內(nèi)存敏感緩存 | 規(guī)范化映射 | 資源清理跟蹤 |
| 實(shí)現(xiàn)類 | - | SoftReference | WeakReference | PhantomReference |
7.2 生命周期圖示

八、實(shí)際應(yīng)用場(chǎng)景
8.1 緩存實(shí)現(xiàn)選擇
強(qiáng)引用緩存:
Map<String, Object> cache = new HashMap<>(); // 可能內(nèi)存泄漏
軟引用緩存:
Map<String, SoftReference<Object>> cache = new HashMap<>(); // 自動(dòng)釋放
弱引用緩存:
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));
}
// 無(wú)需顯式移除,GC會(huì)自動(dòng)清理
}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)存仍在增長(zhǎng)
解決方案:
- 檢查是否有強(qiáng)引用意外保留
- 確保正確使用ReferenceQueue清理
- 使用MAT等工具分析內(nèi)存快照
9.2 引用隊(duì)列處理延遲
問題:對(duì)象已回收但引用未入隊(duì)
解決方案:
- 確保有活躍線程處理隊(duì)列
- 適當(dāng)調(diào)用System.gc()(僅測(cè)試環(huán)境)
- 增加內(nèi)存壓力觸發(fā)GC
9.3 緩存性能優(yōu)化
問題:軟引用緩存頻繁重建
解決方案:
- 調(diào)整JVM內(nèi)存參數(shù)(-Xmx)
- 實(shí)現(xiàn)多級(jí)緩存(強(qiáng)引用+軟引用)
- 使用專業(yè)緩存庫(kù)(Caffeine, Ehcache)
十、總結(jié)
Java的四種引用類型提供了不同層次的對(duì)象生命周期控制:
- 強(qiáng)引用:默認(rèn)選擇,確保對(duì)象長(zhǎng)期存活
- 軟引用:實(shí)現(xiàn)內(nèi)存敏感緩存,平衡性能與內(nèi)存使用
- 弱引用:避免干預(yù)GC行為,適合規(guī)范化映射
- 虛引用:最精細(xì)的回收跟蹤,用于資源清理
正確運(yùn)用這些引用類型可以:
- 預(yù)防內(nèi)存泄漏
- 優(yōu)化內(nèi)存使用
- 實(shí)現(xiàn)高效的緩存策略
- 精確控制資源生命周期
理解這些引用類型的差異和適用場(chǎng)景,是成為Java高級(jí)開發(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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解mybatis批量插入10萬(wàn)條數(shù)據(jù)的優(yōu)化過程
這篇文章主要介紹了詳解mybatis批量插入10萬(wàn)條數(shù)據(jù)的優(yōu)化過程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
詳解Java如何實(shí)現(xiàn)防止惡意注冊(cè)
惡意注冊(cè)通常是指使用自動(dòng)化腳本或者機(jī)器人在短時(shí)間內(nèi)進(jìn)行大量的注冊(cè)行為,這種行為會(huì)對(duì)系統(tǒng)造成壓力,甚至?xí)?dǎo)致系統(tǒng)癱瘓。所以本文為大家總結(jié)了一些防止惡意注冊(cè)的方法,需要的可以參考一下2023-04-04
Zookeeper實(shí)現(xiàn)分布式鎖代碼實(shí)例
這篇文章主要介紹了Zookeeper實(shí)現(xiàn)分布式鎖代碼實(shí)例,Zookeeper?分布式鎖應(yīng)用了其?臨時(shí)順序節(jié)點(diǎn)?的特性,在Zookeeper中創(chuàng)建一個(gè)持久節(jié)點(diǎn)ParentLock,當(dāng)?shù)谝粋€(gè)客戶端要獲取鎖時(shí),在ParentLock節(jié)點(diǎn)下創(chuàng)建一個(gè)臨時(shí)順序節(jié)點(diǎn),需要的朋友可以參考下2023-12-12
ConcurrentMap.putIfAbsent(key,value)用法實(shí)例
這篇文章主要介紹了ConcurrentMap.putIfAbsent(key,value)用法實(shí)例,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02
Java 延遲隊(duì)列的常用的實(shí)現(xiàn)方式
這篇文章主要介紹了Java 延遲隊(duì)列的常用的實(shí)現(xiàn)方式,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下2021-04-04
使用Mybatis遇到的there is no getter異常
這篇文章主要介紹了使用Mybatis遇到的there is no getter異常,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09
Java實(shí)現(xiàn)簡(jiǎn)單的日歷界面
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單的日歷界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
java統(tǒng)計(jì)字符串中指定元素出現(xiàn)次數(shù)方法
這篇文章主要介紹了java統(tǒng)計(jì)字符串中指定元素出現(xiàn)次數(shù)方法,需要的朋友可以參考下2015-12-12

