Java四大引用:强、软、弱、虚引用

📅 2026/6/25 16:11:05
Java四大引用:强、软、弱、虚引用
前言Java 垃圾回收机制是面试核心考点而四大引用类型更是必问内容。JDK1.2 之后Java 对对象引用进行了细分分为强引用、软引用、弱引用、虚引用四种引用决定了对象在 GC 时的回收时机、生命周期各自有独特业务场景。为什么有引用呢因为在Java世界里对象的生死不仅由代码去决定更由引用的类型掌控。通过四种引用可以做到精细化控制对象生命周期实现对象的提前回收、实现高效缓存、有效防止内存泄漏、感知对象回收事件很多人只知道概念但分不清使用场景、不会写测试代码本文结合完整可运行 Demo从定义、特性、代码案例、实际应用全方位拆解看完轻松应对面试。先统一测试实体类User下面各个例子来使用public class User { public int id; public String name; public User(int id, String name) { this.id id; this.name name; } Override public String toString() { return [id id , name name ] ; } }前置底层基础可达性分析引用队列1. GC 判断对象存活可达性分析算法JVM 不再使用计数法采用GC Roots 可达性分析 从 GC Roots本地变量、静态变量、本地方法变量等向下遍历引用链链可达的对象全部存活不可达对象判定为垃圾。 四大引用本质改变「引用链存在时GC 的回收策略」。2. ReferenceQueue 引用队列通用底层机制软 / 弱 / 虚引用都支持绑定ReferenceQueue核心作用 当引用包裹的对象被 GC 回收后该引用对象本身会被 JVM 自动加入队列业务代码可以轮询队列感知对象销毁做后置清理。软 / 弱引用对象回收后引用入队虚引用必须强制绑定队列无队列则完全失去使用价值强引用不支持引用队列。一、强引用只要存在、永不回收1.定义日常代码中最普遍的引用方式Object obj new Object()。 只要强引用链可达垃圾回收器永远不会回收该对象哪怕 JVM 内存溢出 OOM 也不会回收。2. 回收条件必须切断全部强引用链两种方式引用变量赋值null断开引用引用变量离开自身作用域方法执行完毕局部变量销毁 满足条件后对象失去强引用才会进入 GC 候选区。3.完整示例import java.util.concurrent.TimeUnit; public class StrongReferenceTest { public static void main(String[] args) { // 建立强引用 User user new User(1, zhangsan); // 第二个强引用指向同一个对象 User user1 user; // 断开第一个强引用 user null; // 手动触发GC System.gc(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } // user1仍持有强引用对象不会被回收正常打印 System.out.println(user1); } }输出结果[id1, namezhangsan]4.优缺点场景优点普通业务对象默认使用简单直观缺点容易引发内存泄漏集合、静态变量长期持有对象使用场景绝大多数普通业务对象、实体、工具类。二、软引用内存溢出前才回收1.定义由SoftReference实现内存充足时不回收堆内存即将 OOM 前GC 会回收所有仅被软引用关联的对象。如果回收完软引用对象后内存依旧不足才抛出OutOfMemoryError。2.核心特性内存充裕调用get()可以正常获取原始对象内存紧张GC 自动回收软引用对象get()返回null适合做内存敏感缓存内存不够自动释放缓存避免 OOM。3.测试案例启动参数必须配置堆内存限制-Xms10m -Xmx10m固定堆大小制造内存紧张环境public class SoftReferenceTest { public static void main(String[] args) { // 构建软引用注意不能保留外层强引用否则软引用失效 SoftReferenceUser userSoftRef new SoftReference(new User(1, zhangsan)); // 内存充足时可以拿到对象 System.out.println(GC前 userSoftRef.get()); // 分配大字节数组占用堆内存制造内存压力 try { // 申请7M字节数组堆总大小仅10M触发内存回收 byte[] buffer new byte[1024 * 1024 * 7]; } catch (Throwable e) { e.printStackTrace(); } finally { System.out.println(内存紧张后); // OOM之前软引用对象会被回收返回null System.out.println(userSoftRef.get()); } } }运行现象 内存充足时正常打印 User 对象分配大数组后内存不足GC 回收软引用最终输出null。4. 实际应用场景本地缓存框架EHCache、Guava Cache缓存数据使用软引用图片加载、大文件缓存内存不足自动释放图片资源Netty、IO 缓冲区内存缓存平衡性能与内存占用。5.优缺点优点自动平衡缓存性能与内存减少 OOM 概率缺点GC 时机不可控无法精准控制缓存淘汰不适合强一致性缓存。三、软引用只要GC直接回收1. 核心定义由WeakReference实现生命周期极短只要发生 GC无论堆内存是否充足仅被弱引用关联的对象一定会被回收。2. 核心特性下一次 GC 执行弱引用对象直接清空get()方法在 GC 后返回null不阻碍垃圾回收适合临时存储非关键数据。3. 完整测试代码import java.lang.ref.WeakReference; import java.util.concurrent.TimeUnit; public class WeakReferenceTest { public static void main(String[] args) { // 创建弱引用对象无外部强引用 WeakReferenceUser userWeakRef new WeakReference(new User(1, zhangsan)); System.out.println(GC之前 userWeakRef.get()); // 手动触发GC System.gc(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(GC之后 userWeakRef.get()); } }输出结果GC之前[id1, namezhangsan] GC之后null4.适用场景WeakHashMap底层 Key 使用弱引用缓存临时对象、会话信息避免静态 Map 长期持有对象导致内存泄漏当 Key 对象失去所有强引用下次 GC 自动清除 Entry释放内存。四、虚引用仅用于回收通知无法获取对象1. 核心定义又称幽灵引用、幻影引用通过PhantomReference实现是最弱的引用。 两大核心特点get()方法永远返回null无法通过虚引用获取原始对象唯一作用对象被 GC 回收时将虚引用存入ReferenceQueue开发者可监听队列执行回收前清理工作。2. 使用规范虚引用必须绑定ReferenceQueue无队列则无任何意义。3.测试案例import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import java.util.concurrent.TimeUnit; public class PhantomReferenceTest { public static void main(String[] args) throws InterruptedException { User obj new User(1, zhangsan); ReferenceQueueObject queue new ReferenceQueue(); // 创建虚引用绑定引用队列 PhantomReferenceObject phantomRef new PhantomReference(obj, queue); // 切断强引用对象变为仅虚引用关联 obj null; boolean isCollected false; while (!isCollected) { System.gc(); TimeUnit.SECONDS.sleep(1); // 判断虚引用是否入队代表对象已被回收 if (phantomRef.isEnqueued()) { isCollected true; } } System.out.println(虚引用关联对象已被回收虚引用进入队列); } }4.应用场景堆外内存回收监控NIO DirectBuffer 底层使用虚引用释放堆外内存对象销毁前资源清理关闭文件流、释放网络连接、释放本地内存内存监控、对象回收日志埋点追踪对象销毁时机。五、四大引用对比引用类型回收时机获取对象 (get ())核心用途强引用断开所有引用后才回收正常获取普通业务对象默认使用软引用内存即将 OOM 时回收内存充足可获取不足返回 null内存敏感本地缓存弱引用只要 GC 就回收GC 后返回 nullWeakHashMap、临时缓存虚引用对象 GC 完毕引用入队列永远返回 null回收监听、堆外内存释放六、高频面试题拓展1. 软引用和弱引用最大区别软引用会在内存不足时才回收适合缓存弱引用只要 GC 就回收适合不影响内存的临时数据。2. WeakHashMap 为什么不会内存泄漏Key 是弱引用当 Key 对象外部无强引用GC 会自动回收 KeyMap 自动清除对应 Entry不会长期占用内存。3. 虚引用不能获取对象存在意义是什么无法操作对象但可以通过ReferenceQueue感知对象被回收的时机在对象彻底销毁前完成资源释放比如 NIO 堆外内存释放。4. 强引用如何避免内存泄漏集合使用完毕手动清空元素静态变量不要长期持有大对象局部对象使用后置为 null缩短引用生命周期。七、简洁记忆强引用日常 new不置空永远不回收软引用缓存专用内存不够才回收弱引用遇 GC 就回收WeakHashMap 核心虚引用拿不到对象只做回收通知。