Java 中的强引用、软引用、弱引用和虚引用分别是什么?

Sherwin.Wei Lv8

Java 中的强引用、软引用、弱引用和虚引用分别是什么?

回答重点

1)**强引用 (Strong Reference)**:

  • 最常见的引用类型。在 Java 中,默认情况下,任何普通的对象引用都是强引用。
  • 只要一个对象有强引用指向它,垃圾回收器永远不会回收该对象,即使系统内存紧张,宁愿抛出 OutOfMemoryError ,强引用对象也不会被清除。

2)**软引用 (Soft Reference)**:

  • 软引用是用来描述一些还有用但并非必需的对象。
  • 当系统内存不足时,垃圾回收器会回收软引用指向的对象,避免内存溢出。在内存充足时,这些对象不会被回收。
  • 软引用通常用于实现缓存机制,允许程序在不影响性能的情况下利用多余内存。

3)**弱引用 (Weak Reference)**:

  • 弱引用是比软引用更弱的一种引用类型。
  • 只要垃圾回收器发现只有弱引用指向某个对象,该对象会立即被回收,无论系统内存是否充足。
  • 弱引用常用于防止内存泄漏,典型应用场景是 WeakHashMap,它允许缓存的键值对在不再使用时自动清除。

4)**虚引用 (Phantom Reference)**:

  • 虚引用是最弱的一种引用类型。
  • 虚引用对对象的生存时间没有任何影响。如果一个对象仅有虚引用,那么它与没有引用一样,随时会被垃圾回收。
  • 虚引用的主要作用是跟踪对象的垃圾回收状态。在对象被回收时,虚引用会被放入一个 ReferenceQueue,我们可以通过这个队列来执行一些清理或其他后续操作。

扩展知识

软引用的典型应用

软引用适合用于内存敏感的缓存场景。主要优势在于能够根据系统内存情况灵活地释放对象,适用于那些可以重建但需要较大内存的对象。例如,浏览器的图片缓存、文档处理程序的页面缓存等。

1
SoftReference<MyObject> softRef = new SoftReference<>(new MyObject());

上述代码对 MyObject 的引用就是软引用。

弱引用的典型应用

典型应用是 **WeakHashMap**,其中键是弱引用,当键不再被其他强引用持有时,键值对会被自动移除。它的优势在于避免对象长时间占用内存,适合短命对象或者临时缓存数据

1
WeakReference<MyObject> weakRef = new WeakReference<>(new MyObject());

上述代码对 MyObject 的引用就是弱引用。

虚引用的典型应用

虚引用主要用于监控对象的垃圾回收过程,尤其是在需要对被回收的对象做一些资源清理的场景,常用于堆外内存的释放或关闭数据库连接等后处理步骤。它不会影响对象的生命周期,但能在对象被回收时收到通知。

使用虚引用需要与 ReferenceQueue 一起配合:

1
2
ReferenceQueue<MyObject> queue = new ReferenceQueue<>();
PhantomReference<MyObject> phantomRef = new PhantomReference<>(new MyObject(), queue);

  1. 垃圾回收与引用类型的关系
    • 强引用:垃圾回收器不会回收强引用指向的对象。
    • 软引用:系统内存不足时才会回收软引用指向的对象,适合用于缓存。
    • 弱引用:垃圾回收器会立即回收弱引用指向的对象,无论内存状况如何,适合防止内存泄漏。
    • 虚引用:仅用于跟踪对象的回收,完全不影响对象的生命周期。

虚引用与 GC

虚引用的唯一作用就是配合引用队列来监控引用的对象是否被加入到引用队列中,也就是可以准确的让我们知晓对象何时被回收。

还有一点有关虚引用的需要提一下,之前看文章都说虚引用对 gc 回收不会有任何的影响,但是看 1.8 doc 上面说

68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f79657373696d6964612f63646e5f696d6167652f696d672f696d6167652d32303231303232383131323634333133352e706e67.png

简单翻译下就是:与软引用和弱引用不同,虚引用在排队时不会被垃圾回收器自动清除。通过虚引用可访问的对象将保持这种状态,直到所有这些引用被清除或者它们本身变得不可访问。

简单的说就是被虚引用引用的对象不能被 gc,然而在 JDK9 又有个变更记录:

68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f79657373696d6964612f63646e5f696d6167652f696d672f696d6167652d32303231303232383131323730323538322e706e67.png

链接:https://bugs.openjdk.java.net/browse/JDK-8071507

按照这上面说的 JDK9 之前虚引用的对象是在虚引用自身被销毁之前是无法被 gc 的,而 JDK9 之后改了。

我本地只有 JDK11 ,所以看了下 11 doc 的确实改了。

68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f79657373696d6964612f63646e5f696d6167652f696d672f696d6167652d32303231303232383131323732343135382e706e67.png

看起来是把那段删了。所以 JDK9 之前虚引用对引用对象的 GC 是有影响的,9 及之后的版本没影响。

Comments