在 Java 中,引用(Reference) 是对象与内存之间的关联方式,JDK 1.2 后将引用分为 强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference) 四类,核心目的是灵活控制对象的垃圾回收(GC)行为,适配不同的内存使用场景。
一、核心概念总览
| 引用类型 | 核心特征 | GC 触发条件 | 典型使用场景 |
|---|---|---|---|
| 强引用 | 最普通的引用,GC 永远不会回收被强引用的对象 | 只有当强引用链断开时才会被回收 | 普通业务对象(如 new Object()) |
| 软引用 | 内存充足时不回收,内存不足(OOM 前)时回收 | JVM 即将发生 OutOfMemoryError 时 | 缓存(如图片缓存、临时数据缓存) |
| 弱引用 | 无论内存是否充足,只要 GC 扫描到,就会回收被弱引用的对象 | GC 执行时(只要无强引用关联) | 非必需对象(如 ThreadLocal、WeakHashMap) |
| 虚引用 | 最弱的引用,仅用于感知对象被 GC 回收的事件,无法通过虚引用获取对象本身 | 对象被 GC 回收时触发 | 跟踪对象回收、释放堆外内存 |
二、逐类详细说明
1. 强引用(Strong Reference)
(1)定义
日常开发中最常用的引用方式,只要对象被强引用关联,JVM 就不会回收该对象,即使内存耗尽抛出
OutOfMemoryError,也不会主动回收强引用对象。(2)代码示例
java
// 强引用:obj 是指向 Object 对象的强引用
Object obj = new Object();
// 断开强引用(此时对象无强引用关联,GC 可回收)
obj = null;
(3)关键特性
- 强引用是 Java 默认的引用类型,无需显式使用
java.lang.ref包的类; - 即使对象暂时不用,只要有强引用,GC 就不会回收,可能导致内存泄漏(如静态集合持有大量无用对象的强引用);
- 示例:
List<Object> list = new ArrayList<>(); list.add(new Object());只要list不被清空 / 置空,其中的Object就不会被回收。
2. 软引用(Soft Reference)
(1)定义
通过
java.lang.ref.SoftReference 类实现,是「内存敏感」的引用:- 当 JVM 内存充足时,软引用对象不会被回收;
- 当 JVM 内存不足(即将触发 OOM)时,会优先回收所有软引用关联的对象;
- 若回收软引用对象后仍内存不足,才会抛出
OutOfMemoryError。
(2)代码示例
java
import java.lang.ref.SoftReference;public class SoftReferenceDemo {public static void main(String[] args) {// 1. 创建普通对象(强引用)Object cacheObj = new Object();// 2. 包装为软引用SoftReference<Object> softRef = new SoftReference<>(cacheObj);// 3. 断开强引用(仅保留软引用)cacheObj = null;// 4. 尝试获取软引用对象(内存充足时可获取)System.out.println("内存充足时:" + softRef.get()); // 输出:java.lang.Object@xxx// 5. 模拟内存不足(触发 GC 回收软引用)try {// 分配大量内存,触发 OOM 前的 GCbyte[] bigArr = new byte[Integer.MAX_VALUE];} catch (OutOfMemoryError e) {// 内存不足时,软引用对象已被回收System.out.println("内存不足时:" + softRef.get()); // 输出:null}}
}
(3)关键特性
- 软引用常与
ReferenceQueue配合:当软引用对象被 GC 回收后,软引用本身会被加入ReferenceQueue,便于后续清理; - 核心场景:缓存(如手机 App 的图片缓存),既利用缓存提升性能,又能在内存不足时自动释放,避免 OOM。
3. 弱引用(Weak Reference)
(1)定义
通过
java.lang.ref.WeakReference 类实现,比软引用更「弱」:- 无论内存是否充足,只要 GC 扫描到仅被弱引用关联的对象,就会立即回收;
- 弱引用的生命周期仅到下一次 GC 执行前。
(2)代码示例
java
import java.lang.ref.WeakReference;public class WeakReferenceDemo {public static void main(String[] args) {// 1. 创建对象并包装为弱引用Object obj = new Object();WeakReference<Object> weakRef = new WeakReference<>(obj);// 2. 断开强引用obj = null;// 3. GC 前:弱引用仍可获取对象System.out.println("GC 前:" + weakRef.get()); // 输出:java.lang.Object@xxx// 4. 手动触发 GCSystem.gc();// 注意:System.gc() 仅为建议,JVM 不一定立即执行,但多数场景下会触发try {Thread.sleep(100); // 等待 GC 完成} catch (InterruptedException e) {e.printStackTrace();}// 5. GC 后:弱引用对象已被回收System.out.println("GC 后:" + weakRef.get()); // 输出:null}
}
(3)典型应用
WeakHashMap:键(Key)是弱引用类型,当键的强引用断开后,GC 会回收键值对,避免内存泄漏;ThreadLocal:内部通过ThreadLocalMap存储数据,ThreadLocalMap的键是弱引用的ThreadLocal对象,防止ThreadLocal长期持有导致内存泄漏;- 临时数据存储:如配置项的临时缓存,无需长期保留,GC 时自动清理。
4. 虚引用(Phantom Reference)
(1)定义
通过
java.lang.ref.PhantomReference 类实现,是最弱的引用类型,无法通过虚引用获取对象本身(get() 方法永远返回 null),仅用于「跟踪对象被 GC 回收的事件」。(2)核心特性
- 必须与
ReferenceQueue配合使用:当虚引用关联的对象被 GC 回收时,虚引用会被加入ReferenceQueue,程序通过监听ReferenceQueue感知对象已回收; - 虚引用的唯一目的:在对象被回收时执行一些清理操作(如释放堆外内存、关闭文件句柄等),弥补 Java 析构函数(
finalize())的不足。
(3)代码示例
java
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;public class PhantomReferenceDemo {public static void main(String[] args) throws InterruptedException {// 1. 创建引用队列ReferenceQueue<Object> refQueue = new ReferenceQueue<>();// 2. 创建对象并包装为虚引用(必须传入 ReferenceQueue)Object obj = new Object();PhantomReference<Object> phantomRef = new PhantomReference<>(obj, refQueue);// 3. 断开强引用obj = null;// 4. 虚引用的 get() 方法永远返回 nullSystem.out.println(phantomRef.get()); // 输出:null// 5. 手动触发 GCSystem.gc();Thread.sleep(100);// 6. 检查引用队列:虚引用已被加入(说明对象已被回收)System.out.println("虚引用是否入队:" + (refQueue.poll() == phantomRef)); // 输出:true}
}
(4)典型应用
- 堆外内存释放:Java NIO 的
DirectByteBuffer会占用堆外内存,虚引用可跟踪其回收,确保堆外内存被及时释放; - 替代
finalize():finalize()执行时机不可控,且可能导致对象复活,虚引用是更可靠的对象回收回调方式。
三、关键对比与总结
- 回收优先级:强引用 > 软引用 > 弱引用 > 虚引用(GC 优先回收虚引用对象,最后回收强引用对象);
- 使用方式:
- 强引用:直接赋值(
Object obj = new Object()); - 软 / 弱 / 虚引用:需通过
java.lang.ref包的对应类包装,其中虚引用必须配合ReferenceQueue;
- 强引用:直接赋值(
- 核心设计目标:
- 强引用:保证对象的正常使用,不被 GC 回收;
- 软引用:内存友好的缓存,避免 OOM;
- 弱引用:自动清理非必需对象,减少内存泄漏;
- 虚引用:感知对象回收,处理堆外资源。
四、常见面试 / 使用误区
- 误区 1:
System.gc()一定会触发 GC?❌ 纠正:System.gc()只是向 JVM 发送 GC 建议,JVM 可忽略(如新生代内存充足时),因此弱 / 软引用的回收时机不可精确控制; - 误区 2:弱引用对象被回收后,弱引用本身会自动失效?
❌ 纠正:弱引用对象被回收后,弱引用本身不会消失,需通过
ReferenceQueue手动清理,否则会导致Reference对象堆积; - 误区 3:虚引用能获取对象?
❌ 纠正:虚引用的
get()方法永远返回null,仅能通过ReferenceQueue感知对象回收。