软引用和弱引用的区别 | Java Debug 笔记

793 阅读6分钟

本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看 活动链接

问题

软引用和弱引用之间的区别?

java.lang.ref.WeakReferencejava.lang.ref.SoftReference 之间的区别?

回答

回答 1

以下回答节选自 Ethan Nicholas's Blog: Understanding Weak References

  • 弱引用

简单来讲,弱引用指不足以强制对象保留在内存中的引用。我们可以垃圾回收期来对它进行回收,因此我们不用手动去释放它。创建一个弱引用的方式如下:

WeakReference weakWidget = new WeakReference(widget);

然后我们就可以在程序中其他地方使用 weakWidget.get() 来获取实际的 Widget 对象。当然了,弱引用是会被垃圾回收器强制回收的,因此有时候我们调用 weakWidget.get() 返回的结果可能是 null

  • 软引用

软引用和弱引用完全相同,但它不急于释放它所引用的对象。在下一个垃圾收集周期来临时,弱引用对象将被回收,但软引用对象通常会保留一段时间。软引用的作用并非要与弱引用何所区别,但在实际场景中,主要 JVM 内存充足,通常会保留软引用对象。这就使得它们可以作为缓存,而由垃圾回收器来判断何时释放软引用对象。

回答 2

弱引用会被强制回收,如果垃圾回收器发现一个对象是弱引用,则它将被立即回收。因此,它们有助于保持对一个对象的引用,而您的程序也需要保留(强引用)某些“相关信息”,比如类的缓存反射信息,或对象的包装等等。任何在与之关联的对象之后保留没有意义的东西都将被垃圾回收器回收。当弱引用被回收时,它会被放入一个引用队列中,您的代码会轮询这个队列的某个位置,并且它也会丢弃相关的对象。也就是说,您保留了关于某个对象的额外信息,但是一旦该对象所引用的对象消失,就不再需要这些信息了。实际上,在某些情况下,我们甚至可以在弱引用子类中子类化弱引用,并在弱引用子类的字段中保留与该对象相关的额外信息。弱引用的另一个典型用法是与 Maps一起使用,以保持规范化实例。

另一方面,软引用对于缓存外部的、可重新创建的资源非常有用,因为 GC 通常会延迟清除这些资源。虽然可以保证在抛出 OutOfMemoryError 之前,所有的软引用都会被清除,所以理论上它们不会引起 OOME.

典型的实例是从文件中保留内容的解析形式。我们需要实现一个系统,在这个系统中来加载文件、解析文件,并保留一个到解析表示的根对象的软引用。下次需要该文件时,将尝试通过软引用来检索该文件。如果你可以检索到该文件,就省去了重新加载/解析的过程,如果 GC 同时清除了它,那就需要重新加载它。这样,我们就可以重复利用可用内存进行性能优化,但是不要冒险使用 OOME。

保留一个软文件参考不会导致一个文件本身。另一方面,如果您错误地使用软引用来执行任务,那么可以使用弱引用(也就是说,您保留与某个对象以某种方式强引用的关联信息,并在引用对象被回收时丢弃它) ,您可以运行 OOME 作为您的代码,轮询引用队列并丢弃关联的对象,这些对象可能不会及时运行。

因此,使用何种引用取决于需求场景——如果您缓存构造成本高昂的信息,但仍然可以从其他数据重构,那么则推荐使用软引用。而如果您想保留对某些数据的规范实例的引用,或者您希望拥有对某个对象的引用而不“拥有”它(从而防止它被垃圾回收器回收) ,那么则可以使用弱引用。

回答 3

Java 中,引用从强到弱的排序为:强引用、软引用、弱引用、虚引用。

  1. 强引用

使用最普遍的引用,也是我们日常使用的大多数引用,如 String str = "村雨遥"。若一个对象具有强引用,就 相当于生活中必备的物品,垃圾回收器绝对不会回收它,当内存空间不足时,JVM 宁愿抛出 OOM 错误,也不会随意回收具有强引用的对象来解决内存不足问题,因此强引用是造成 Java 内存泄露 的主要原因之一。

  1. 软引用

若一个对象只具有软引用,则 相当于生活中可有可无的物品。若内存空间充足,则垃圾回收器不会回收它,一旦内存空间不足,则会回收这些对象的内存。只要垃圾回收器未回收这个对象的内存,则该对象能够被程序使用,通过使用软引用可以实现内存敏感的高速缓存,加速 JVM 对垃圾内存的回收速度,同时维护系统的运行安全,防止 OOM 等问题的产生

  1. 弱引用

一若个对象只具有弱引用,则 相当于生活中可有可无的物品。 软引用和弱引用的区别在于:只拥有弱引用的对象具有更短暂的生命周期,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现具有弱引用的对象,则无论当前内存空间是否充足,都会回收它的内存。 但一般垃圾回收器的线程优先级很低,因此不会很快就回收具有弱引用的对象。

此外 软引用和弱引用都可以和一个引用队列联合使用,一旦他们所引用的对象被垃圾回收,JVM 就会将这个引用加入到相关的引用队列中。

  1. 虚引用

形同虚设的一个引用,不会决定对象的声明周期,一个对象仅持有虚引用,则任何时候都可能被垃圾回收器回收,主要用来跟踪对象被垃圾回收的活动

虚引用与软引用和弱引用的区别虚引用必须和引用队列联合使用。当垃圾回收器准备回收一个对象时,若发现该对象具有虚引用,则会在回收该对象的内存前,将该虚引用加入到与之关联的引用队列中。程序能够通过判断引用队列中是否已经加入虚引用,来了解被引用的对象是否将要被垃圾回收器回收。

  1. 总结
引用类型回收阶段
强引用发生 GC 时不被回收
软引用有用但非必须的对象,发生内存溢出前被回收
弱引用有用但非必须的引用,下一次 GC 时被回收
虚引用无法通过虚引用获取对象,用 PhantomReference 实现虚引用,其用途是在 GC 时返回一个通知

出处

文章翻译自 Stack Overflow:What's the difference between SoftReference and WeakReference in Java?