1. 概述

本文将深入探讨 Java 中的弱引用概念。我们将解释弱引用的定义、使用场景以及正确的使用方法,帮助开发者更好地理解这一底层机制。

2. 弱引用原理

弱引用对象在仅存在弱引用可达时会被垃圾回收器清除。

弱引用可达性意味着对象既没有强引用也没有软引用指向它,只能通过遍历弱引用才能访问到该对象。

垃圾回收器处理弱引用的流程:

  1. 首先清除弱引用,使被引用对象不再可访问
  2. 然后将引用放入关联的引用队列(如果存在)
  3. 同时,之前弱引用可达的对象将被终结

2.1 弱引用 vs 软引用

弱引用和软引用的区别常让人困惑。软引用本质上是一个大型 LRU 缓存,当被引用对象很可能在近期被重用时使用软引用

关键区别: ✅ 软引用作为缓存,即使被引用对象不可达,仍可能保留 ✅ 软引用被回收的条件:

  • 被引用对象不是强可达
  • 软引用最近未被访问 ❌ 弱引用在被引用对象被回收后立即失效

简单来说:软引用可能存活几分钟甚至几小时,而弱引用在被引用对象消失后立即失效。

3. 典型应用场景

根据 Java 文档,弱引用最常用于实现规范化映射。规范化映射是指只保存特定值的一个实例,通过查找映射重用现有对象而非创建新对象。

最著名的应用是 WeakHashMap

  • 实现 Map 接口
  • 每个键存储为弱引用
  • 当垃圾回收器移除键时,关联的实体也会被删除

⚠️ 更多细节可参考我们的 WeakHashMap 指南

另一个应用场景是监听器泄漏问题

  • 发布者(主题)持有所有订阅者(监听器)的强引用
  • 当监听器无法正确取消订阅时出现问题
  • 监听器因发布者的强引用无法被回收,导致内存泄漏

解决方案:让主题持有观察者的弱引用,允许观察者被回收(注意:这不是完整解决方案,会引入其他问题)。

4. 弱引用操作指南

弱引用通过 java.lang.ref.WeakReference 类实现。初始化时需传入被引用对象,可选关联引用队列:

Object referent = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();

WeakReference<Object> weakReference1 = new WeakReference<>(referent);
WeakReference<Object> weakReference2 = new WeakReference<>(referent, referenceQueue);

核心操作方法:

  • get() 获取被引用对象
  • clear() 手动清除引用
Object referent2 = weakReference1.get();
weakReference1.clear();

安全使用模式(与软引用相同):

Object referent3 = weakReference2.get();
if (referent3 != null) {
    // GC 尚未回收实例
} else {
    // GC 已清除实例
}

5. 总结

本文深入分析了 Java 弱引用的底层机制,重点介绍了最常见的使用场景。掌握弱引用有助于解决内存管理问题,特别是在构建缓存和事件系统时。


原始标题:Weak References in Java | Baeldung