java强引用、软引用、弱引用、虚引用

在 Java 中,引用类型决定了对象在垃圾回收时的生命周期。以 User 对象为例,分别演示强引用、软引用、弱引用、虚引用的用法及回收特性。


1. 强引用(Strong Reference)

特点:最常用的引用,只要强引用存在,对象永远不会被 GC 回收。

java

sql 复制代码
User user = new User("Alice");
// 此时 user 是强引用,User 对象不会被回收
user = null;   // 切断引用后,对象才可能被回收

2. 软引用(SoftReference)

特点 :通过 SoftReference 实现,当内存充足时保留,仅在内存即将耗尽(即将 OOM)时才会被回收。适合实现内存敏感的高速缓存。

java

csharp 复制代码
// 创建软引用
SoftReference<User> softRef = new SoftReference<>(new User("Bob"));

// 获取对象
User user = softRef.get();   // 内存充足时返回 User 对象,OOM 前可能返回 null

// 模拟内存紧张时,GC 会回收软引用指向的对象
System.gc();   // 不保证立即回收,但若内存紧张则会回收

3. 弱引用(WeakReference)

特点 :通过 WeakReference 实现,只要发生 GC,无论内存是否充足,对象都会被回收 。常用于规范映射(如 WeakHashMap)以及 ThreadLocal 的 key 设计。

java

csharp 复制代码
// 创建弱引用
WeakReference<User> weakRef = new WeakReference<>(new User("Charlie"));

// 获取对象(可能为 null)
User user = weakRef.get();   // GC 前存在,GC 后为 null

// 手动触发 GC,对象会被回收
System.gc();
System.out.println(weakRef.get());   // 输出 null

4. 虚引用(PhantomReference)

特点 :最弱的引用,通过 PhantomReference 实现。无法通过 get() 获取对象实例,仅用于跟踪对象被回收的通知 (必须配合 ReferenceQueue)。常用于资源释放监控或对象回收前的清理。

java

csharp 复制代码
// 必须配合引用队列
ReferenceQueue<User> queue = new ReferenceQueue<>();

// 创建虚引用
PhantomReference<User> phantomRef = new PhantomReference<>(new User("David"), queue);

// 无法获取对象
User user = phantomRef.get();   // 永远返回 null

// 当虚引用指向的对象被回收后,该引用会被加入队列
System.gc();
Reference<? extends User> ref = queue.poll();
if (ref != null) {
    // 执行回收后的清理工作
    System.out.println("User 对象已被回收");
}

5. 总结对比(以 User 对象为例)

引用类型 示例代码 回收时机 典型用途
强引用 User u = new User(); 永不回收(除非引用置空) 普通对象
软引用 SoftReference<User> s = new SoftReference<>(new User()); 内存不足时回收 缓存(如图片缓存)
弱引用 WeakReference<User> w = new WeakReference<>(new User()); 每次 GC 都可能回收 WeakHashMap、ThreadLocal 的 key
虚引用 PhantomReference<User> p = new PhantomReference<>(new User(), queue); 任何时候都可能回收,且 get() 返回 null 对象回收跟踪、资源释放

6. 延伸:ThreadLocal 为什么用弱引用?

ThreadLocal 内部 ThreadLocalMapkey 使用弱引用 ,目的是当 ThreadLocal 实例不再被外部强引用时,GC 可以回收它,避免 ThreadLocal 对象本身的内存泄漏。但 value 仍是强引用 ,所以仍需要手动调用 remove() 清除 value,防止线程池场景下出现内存泄漏。

java

scala 复制代码
// ThreadLocalMap.Entry 源码简写
static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        super(k);   // key 是弱引用
        value = v;  // value 是强引用
    }
}
相关推荐
冰暮流星6 分钟前
flask之定义URL
后端·python·flask
程序员爱钓鱼1 小时前
为什么学习 Rust?Rust 能做什么?
后端·rust
掘金者阿豪2 小时前
Docker命令太多记不住?用Portainer把容器管理变成可视化操作
后端
用户69371750013842 小时前
AI Agent 里的 Loop 到底是什么?
android·前端·后端
Rain的Java大神之路2 小时前
高并发下的抢优惠券如何设计
后端
钟智强2 小时前
AWS CloudGoat 实战:一个 SSRF 如何撬动整个云账户
后端
Ai拆代码的曹操3 小时前
@Transactional 写在 private 方法上,事务根本没生效
后端
云技纵横3 小时前
@Async 默认执行器又炸了:SimpleAsyncTaskExecutor 为什么不是线程池?
后端·面试
程序员Agions3 小时前
Fypro 新手快速上手与实战指南
后端
舒心的云朵3 小时前
用三张图片详解Asp.Net 全生命周期
后端·asp.net