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 是强引用
    }
}
相关推荐
我是大猴子1 小时前
Spring代理类为何依赖注入失效?
java·后端·spring
码事漫谈2 小时前
手把手带你部署本地模型,让你Token自由(小白专属)
前端·后端
码农BookSea3 小时前
ReAct:让大模型学会边想边做
后端·ai编程
码农BookSea3 小时前
10分钟掌握 JSON-RPC 协议,面试加分、设计不踩坑
后端
凤年徐3 小时前
C++手撕红黑树:从0到200行,拿下STL map底层核心
c++·后端·算法
IT_陈寒3 小时前
Python的列表推导式里藏了个坑,差点让我加班到凌晨
前端·人工智能·后端
卷无止境5 小时前
podman与docker的区别和生产环境最佳实践
后端
程途知微5 小时前
ConcurrentHashMap线程安全实现原理全解析
java·后端
Mars酱5 小时前
1分钟编写贪吃蛇 | JSnake贪吃蛇单机版
java·后端·开源
卷卷说风控5 小时前
养了10年风控,今年开始养「虾」了
后端