Java 中的对象引用有四种类型:强引用、软引用、弱引用和虚引用,它们有什么区别以及各自的用途是什么呢?

强引用(Strong Reference)

强引用就是指在程序代码中普遍存在的、类似于 Object obj = new Object() 这种直接关联 new 对象的引用,只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。

软引用(Soft Reference)

软引用是用来描述一些有用但并非必需的对象。如果一个对象只有软引用相关联,则只有当 JVM 内存空间不足时,垃圾收集器才会回收这些对象;当内存空间充足时,垃圾收集器就不会回收它。

也就是说,垃圾收集器会在虚拟机抛出 OutOfMemoryError 内存溢出异常之前回收软引用对象,而且虚拟机会尽可能地优先回收长时间闲置不用的软引用对象,对于那些刚产生或者刚使用过的对象会尽可能保留。这也是采用引用队列 ReferenceQueue 的原因。

从 JDK 1.2 开始提供了 SoftReference 类来封装实现软引用:

1
2
// 通过软引用 stuSoftRef 关联一个 Student 对象
SoftReference<Student> stuSoftRef = new SoftReference<>(new Student("张三", 18));

一般可以用软引用来实现内存敏感的高速缓存,如 MyBatis 源码之缓存模块-SoftCache

弱引用(Weak Reference)

弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,且弱引用的对象具有更短暂的生命周期。当垃圾收集器发现了只具有弱引用的对象时,无论当前内存空间是否足够,都会回收它的内存。不过由于垃圾收集器是一个优先级很低的线程,因此不一定会很快就发现那些只具有弱引用的对象。

从 JDK 1.2 开始提供了 WeakReference 类来封装实现软引用:

1
2
// 通过弱引用 stuWeakRef 关联一个 Student 对象
SoftReference<Student> stuWeakRef = new SoftReference<>(new Student("张三", 18));

弱引用一般也用于缓存的实现,应用示例如:

虚引用(Phantom Reference)

虚引用是最弱的一种引用关系,虚引用并不会影响对象的生命周期。如果一个对象只有虚引用相关联,那么它就和没有任何引用一样,在任何时候都可能被垃圾收集器回收。

在程序中不能通过虚引用来取得一个对象实例,为一个对象设置虚引用关联的唯一目的就是,能在这个对象被垃圾收集器回收时收到一个系统通知。这个通知的功能可以用于跟踪对象被垃圾收集器回收的活动、在 NIO 中管理直接内存对象 DirectByteBuffer 等。

从 JDK 1.2 开始提供了 PhantomReference 类来封装实现软引用:

1
2
3
4
ReferenceQueue queue = new ReferenceQueue();

// 通过虚引用 stPhantomRef 关联一个 Student 对象
PhantomReference<Student> stPhantomRef = new PhantomReference(new Student("张三", 18), queue);

这里需要注意的是,虚引用必须和引用队列(ReferenceQueue)联合使用,当垃圾收集器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。然后在回收引用队列中虚引用所引用的对象之前,就可以检测到通知进而采取必要的行动。