何时在Java中使用弱引用和虚引用

8

我阅读了很多文章,但是不明白——在实践中,我什么时候需要使用弱引用(Weak References)和虚引用(Phantom References)?据我所知,软引用(Soft References)是缓存的好选择。但是弱引用和虚引用,我不知道什么时候使用。请提供一些实际任务的示例,我们需要在什么情况下使用它们。


1
您不需要使用幽灵引用。这些是供内部使用的。您可以在任何使用软引用的地方使用弱引用,除了它们的寿命更短。当只有另一个对象也有引用时,您可以使用弱引用,例如监听器。 - Peter Lawrey
5个回答

8
您可以像您所说的那样简单地使用弱引用作为缓存。 那么,幽灵引用有什么好处呢? 我只知道两种情况需要使用它们:首先,它们允许您确定对象何时从内存中删除。 实际上,这是唯一的方法。 这通常不是很有用,但在某些非常特定的情况下可能会派上用场,例如操作大型图像:如果您确切地知道图像应该被垃圾回收,您可以等待它实际上被垃圾回收后再尝试加载下一个图像,从而减少恼人的OutOfMemoryError。
其次,幽灵引用避免了finalize()的一个基本问题:finalize()方法可以通过创建对它们的新强引用来“复活”对象。那又怎样呢? 嗯,问题在于覆盖finalize()的对象现在必须在至少两个单独的垃圾收集周期中被确定为垃圾才能被收集。 当第一个周期确定它是垃圾时,它就有资格进行finalize()。 由于(虽然很小,但不幸的是真实的)可能性对象在finalize()期间“复活”,因此垃圾收集器必须再次运行,然后才能实际删除对象。 由于finalize()可能没有及时发生,因此在对象等待finalize()期间可能已经发生了任意数量的垃圾收集周期。 这可能会导致在清理垃圾对象时出现严重延迟,并且这就是为什么即使大多数堆都是垃圾,您也可能会遇到OutOfMemoryErrors的原因。
有关更多详细信息,请参见此页面: http://weblogs.java.net/blog/2006/05/04/understanding-weak-references

4
弱引用对于缓存来说是完全无用的。 - Marko Topolnik
1
一个可终结对象被复活的概率不是“微乎其微”——它是100%;一个只因存在finalize方法而不会消失的可终结对象,在其终结器开始运行时,将在堆栈上放置对它的强引用。在该引用(以及所有其他根引用)停止存在之前,该对象无法被删除。 - supercat
根据OoME JavaDoc的说明,当Java虚拟机因为内存不足而无法分配对象,并且垃圾收集器无法提供更多内存时,就会抛出此异常。因此,无论大图像是否在内存中,它都会尝试在失败之前将其清除。否则,您应该知道是否仍然持有对它的引用。 - Sled

5
基本上,当您想要将一些额外的数据与源代码不在您控制之下的对象相关联时,可以使用弱引用。通过使用弱引用,您将元对象的生命周期与主对象的生命周期耦合在一起。
幻影引用的主要用途是实现自己的终结器线程,而不会出现与默认机制相关的危险,该机制被迫使对应该无法访问的对象的引用对终结器代码可用。
软引用主要用于缓存,但正如另一篇帖子中所说,它们在实践中可能会产生相当灾难性的影响,从而破坏了缓存的意义。主要的GC(将清除您的软引用)通常不会发生,直到对您的应用程序性能施加压力。这是您最需要缓存的时间,也是您最有可能一次失去它的时间。

根据原帖,他想知道何时使用“Phantom”引用和“Weak References”……你的回复并没有完全回答他的问题。 - Abimbola Esuruoso
不,你误解了问题,问题是何时使用弱引用和虚引用。这不是关于如何在它们之间进行选择,而是何时使用任何一种。因此,即使这个答案没有涉及虚引用(截至您发表评论的时间;现在它已经涉及到了),它所陈述的一切都是相关且正确的。 - Marko Topolnik
我同意,新的回答涵盖了该主题。 - Abimbola Esuruoso

3

这个文章对这个问题有很好的回答。


3

我认为这篇文章对你的问题有很好的回答。

Java中弱引用和软引用有什么区别?

基本上,软引用比弱引用稍微强一些。 弱引用将在下一个GC周期中被丢弃,而软引用将保留在内存中,直到存在内存压力并且JVM希望尽可能多地回收内存。

您应该考虑一下具有参考价值的内容对于程序实现的重要性。如果重新创建参考非常便宜,那么我会倾向于使用WeakReference,但如果它来自数据库的值,则可能倾向于软引用,因为除非您真正需要,否则不想重新运行查询。


谢谢。但我理解这些引用类型之间的差异。我不明白它们可以应用于什么具体任务。 - VladislavLysov
我添加了我的看法,关于何时使用Weak引用和Soft引用。 - jjathman

0

SoftReference对象在所有WeakReference对象被垃圾回收之前不会被回收。

因此,将不太重要的对象放入WeakReference对象中,并使用SoftReference对象来保存更重要的对象。

考虑到这些事实,您应该根据垃圾回收的需要使用好的引用对象。首先回收WeakReference,然后是SoftReference,最后是PhantomReferences。

文档说:

  • Soft references用于实现内存敏感的缓存
  • Weak references用于实现不能防止其键(或值)被回收的规范化映射

顺便说一下,在某些情况下,为了缓存目的,使用WeakReference而不是SoftReference可能是一个好主意,因为缓存可能在内存中很重,因此需要清理。

对于PhantomReference,使用方式不同。它们用于以比Java终结机制更灵活的方式安排临死清理操作。

这篇文章稍微详细地介绍了PhantomReference的用途。


1
你对于弱引用和软引用的理解基本正确。但是你对于虚引用的理解是错误的。虚引用只有在替代finalize方法(也称资源清理)时才有用。它们与缓存或对象何时被垃圾回收无关。 - jtahlborn
你倒数第二句话似乎暗示你会根据它们可能被收集的时间来选择各种类型。我的观点是,你使用幻影引用与弱/软引用完全不同,因此那句话毫无意义(是的,你关于收集顺序的观点是正确的,但那不是我的重点)。 - jtahlborn
1
弱引用不会延迟任何操作——只要没有对所指对象的“正常”引用(即不是强引用),它们就会被清除。这就是为什么它们在作为缓存时会失败——对象将进入年轻代,并且可以在几秒钟内被清除,而空闲堆仍然充足。 - Marko Topolnik
1
考虑这个陈述:直到你度过了青少年时期,你才算成年。青少年会延迟你的成年吗? - Marko Topolnik
1
最后一句话:大多数关于哪种引用更适合缓存的讨论只是纸上谈兵,因为在实际项目中,你永远不会尝试从头开始实现缓存:你会使用经过实战验证的实现,比如ehcache。良好的缓存充满了微妙之处,你不想自己重新发明轮子,堆上对象的生命周期绝不是提供良好缓存性能的唯一方式。现代缓存甚至不使用堆进行缓存,而是使用堆外存储,因为它们希望完全控制缓存对象的生命周期。 - Marko Topolnik
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接