01 引言
在Java的世界里,对象的生死不仅由代码决定,更由引用的类型掌控。对象产生需要内存去支撑,而在内存固定的情况下,对象的存活的生命周期对于内存的管理至关重要。不使用的对象,垃圾回收器根据可达性分析、三色标记等不同的算法等默默回收着。
而Java
的这四大引用,也影响着垃圾回收器对垃圾回收的时机。理解这四种引用,可以更精确的控制对象的回收,也是你进阶高级开发的必经之路!
02 为什么需要不同的引用类型
Java
的垃圾回收(GC
)看似自动,但不当的对象引用会导致内存泄漏或OOM
崩溃。细粒度的对对象的控制可以应用到不同的场景。通过四种引用类型,我们至少可以做到下面几点:
- 精细化控制对象生命周期,实现对象的提前回收
- 实现高效缓存,如图片缓存
- 有效防止内存泄漏,如
ThreadLocal
- 感知对象回收事件
03 四大引用详解
不同的引用类型,受垃圾回收器回收的影响各不相同。
3.1 强引用
强引用(Strong Reference
),默认的引用类型,也是最常见的。如果一个对象被强应用,这要这种引用还存在就不会被垃圾回收器回收。
// 强引用
Object objet = new Object();
其中object
就是一个强引用的对象,不会被垃圾回收器回收。对象的一般都是在方法内进行的,所以其作用域只会在方法栈上,只有到强引用跳出了其作用域,也就是弹出方法栈,那么该引用就被在合适的时机被垃圾回收器回收。
从图中可以看到,方法内的垃圾回收并不会回收强引用的对象。
简单总结一下:
- 特点:只要强引用存在,对象绝不会被GC回收
- 风险:
obj = null
手动释放前,即使内存不足也不会回收,可能引发OOM - 场景:日常开发中最常用的引用类型
3.2 软引用
软引用(Soft Reference),被称为内存敏感的安全气囊。如果一个对象被软引用,那么在JVM
内存溢出之前,是不会被垃圾回收器回收的;只有到JVM
内存不够时,才会被垃圾回收器回收这个对象。
软引用的对象需要借助java.lang.ref.WeakReference
,实现对象的软引用。
可以看到,调用GC
之后,并不会回收引用对象。
小结一下:
- 特点:内存充足时保留对象;内存不足时优先回收
- 回收时机:GC后发现内存仍不足 → 回收所有软引用
- 场景:图片/数据缓存(Android中Glide的缓存机制)
3.3 弱引用
弱引用(Weak Reference
),被称为随时消失的"临时工"。如果一个对象被弱引用,那么它的生命周期只能存活在下一次GC
之前,只要GC
启动,就会回收所有的弱引用对象。
弱引用的对象需要借助java.lang.ref.WeakReference
,实现对象的弱引用。
可以看到,调用GC
之后,弱引用对象就会被回收。
小结一下:
- 特点:无论内存是否充足,下一次GC必然回收
- 场景:
WeakHashMap
实现临时缓存(Key被回收时自动移除Entry)
3.4 虚引用
虚引用(Phantom Reference),被称为对象回收的"墓碑"。虚引用是所有引用中最弱的一种,是无法直接获取到弱引用的中的对象的。其存在就是为了监听关联的虚引用被GC
之后,收到系统的通知。
弱引用的对象需要借助java.lang.ref.PhantomReference
,实现对象的弱引用。
// 引用队列
ReferenceQueue<Object> rq = new ReferenceQueue<>();
// 弱引用
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), rq);
System.out.println(DateUtil.now() + " 虚引用方法已执行!");
// 3s后GC
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.gc();
}
}, 3000);
// 监控回收事件的新线程
new Thread(() -> {
try {
rq.remove(); // 阻塞直到对象被回收
System.out.println(DateUtil.now() + " 对象已被GC回收!");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
// 阻塞主线程
System.in.read();
执行结果
可以看到,调用GC
之后,虚引用也会被直接回收,同时可以唤醒ReferenceQueue.remove()
方法,通知被GC
。
小结一下:
- 特点:
- 无法通过
get()
获取对象(永远返回null) - 必须配合
ReferenceQueue
使用 - 对象回收时收到系统通知
- 无法通过
- 场景:
- 精准监控大对象回收(如
1GB
的缓存被回收时触发重建) - 资源清理(如
JDK
的Cleaner机制替代finalize())
- 精准监控大对象回收(如
04 四大引用对比总结
引用类型 | 回收时机 | 是否阻塞OOM | 常见场景 |
---|---|---|---|
强引用 | 永不回收 | ❌ 可能引发 | 普通对象持有 |
软引用 | 内存不足时 | ✅ 避免 | 缓存 |
弱引用 | 下次GC时 | ✅ 避免 | 防内存泄漏 |
虚引用 | GC后收到通知 | ✅ 避免 | 对象回收监听 |
05 小结
在日常开发中最常见的还是强引用,而其他引用用的比较少。其他引用主要用在架构的设计或者工具的设计上。合理的使用四大引用,可以让你的代码能力更上一层楼。