197

我已经阅读了这篇关于 Java 中不同类型的引用(强、软、弱、幻)的文章,但我并不真正理解它。

这些引用类型之间有什么区别,每种类型何时使用?

4

7 回答 7

156

Java 提供了两种不同类型/类的引用对象。Weak Reference Objects 可以进一步分为softphantom

  • 强的
  • 虚弱的
    • 柔软的
    • 幻影

让我们逐点进行。

强引用对象

StringBuilder builder = new StringBuilder();

这是参考对象的默认类型/类,如果没有另外指定:builder是强参考对象。这种引用使得被引用的对象不符合 GC 的条件。也就是说,每当一个对象被强引用对象链引用时,它就不能被垃圾回收。

弱引用对象

WeakReference<StringBuilder> weakBuilder = new WeakReference<StringBuilder>(builder);

弱引用对象不是引用对象的默认类型/类,要使用它们应该像上面的示例一样明确指定。这种引用使引用对象符合 GC 条件。也就是说,如果StringBuilder内存中对象的唯一可访问引用实际上是弱引用,则允许 GC 对StringBuilder对象进行垃圾收集。当内存中的对象只能被弱引用对象访问时,它会自动符合 GC 条件。

弱点程度

可以招募两种不同级别的弱点:软弱幻影

软引用对象基本上是一个引用对象,它会更多地保留在内存中:通常,它会抵抗 GC 循环,直到没有可用内存并且存在风险OutOfMemoryError(在这种情况下,它可以被删除)。

另一方面,幻影参考对象仅在准确知道对象何时从内存中有效删除时才有用:通常它们用于修复奇怪的 finalize() 复活/复活行为,因为它们实际上并不返回对象本身,而是仅有助于跟踪他们的记忆存在

弱引用对象是实现缓存模块的理想选择。事实上,当对象/值不再被强引用链访问时,可以通过允许 GC 清理内存区域来实现一种自动驱逐。一个例子是WeakHashMap保留弱键。

于 2014-05-07T13:50:30.090 回答
80

弱参考:

简而言之,弱引用是一种强度不足以强制对象保留在内存中的引用。弱引用允许您利用垃圾收集器为您确定可达性的能力,因此您不必自己做。

软参考:

软引用与弱引用完全一样,只是它不太急于丢弃它所引用的对象。只能弱可达的对象(对它的最强引用是 WeakReferences)将在下一个垃圾回收周期被丢弃,但软可达的对象通常会保留一段时间。

幻影参考:

幻像引用与 SoftReference 或 WeakReference 完全不同。它对其对象的控制非常脆弱,以至于您甚至无法检索该对象——它的 get() 方法总是返回 null。这种引用的唯一用途是跟踪它何时进入 ReferenceQueue,因为此时您知道它指向的对象已死。

本文摘自:https ://weblogs.java.net/blog/2006/05/04/understanding-weak-references

于 2012-05-10T07:44:51.043 回答
26

SoftReference和之间的简单区别WeakReferenceAndroid Developer提供。

SoftReferencea和 a之间的区别WeakReference是决定清除引用并将其入队的时间点:

  • ASoftReference应该尽可能晚地被清除和排队,也就是说,以防 VM 有内存不足的危险。

  • WeakReference一旦知道弱引用,A就可以被清除并入队。

于 2012-04-08T12:01:28.860 回答
19

这篇文章对于理解强引用、软引用、弱引用和虚引用非常有帮助。


给大家总结一下,

如果您对对象有强引用,则 GC(垃圾收集器)永远无法收集/回收该对象。

如果您只有对对象的弱引用(没有强引用),那么该对象将在下一个 GC 循环中被 GC 回收。

如果你只有对一个对象的软引用(没有强引用),那么只有当 JVM 内存不足时,GC 才会回收该对象。

我们创建对对象的幻像引用,以跟踪对象何时进入ReferenceQueue. 一旦你知道你可以执行细粒度的终结。(这将使您免于意外复活该对象,因为幻像引用不给您引用者)。我建议您阅读这篇文章以深入了解这一点。


所以你可以说,强引用具有终极力量(GC永远无法收集)

软引用比弱引用更强大(因为它们可以逃避 GC 循环,直到 JVM 内存不足)

弱引用甚至不如软引用强大(因为它们无法逃脱任何 GC 循环,并且如果对象没有其他强引用将被回收)。


餐厅类比

  • 服务员 - GC
  • 你 - 堆中的对象
  • 餐厅区域/空间 - 堆空间
  • 新客户 - 想要在餐厅用餐的新对象

现在,如果您是一个强大的客户(类似于强参考),那么即使餐厅有新客户进来或发生了什么事,您也永远不会离开您的餐桌(堆上的内存区域)。服务员无权告诉您(甚至要求您)离开餐厅。

如果你是软顾客(类似于软参考),那么如果餐厅有新顾客进来,服务员不会要求你离开餐桌,除非没有其他空桌子可以容纳新顾客。(换句话说,只有当有新客户进来并且没有其他桌子可供该新客户使用时,服务员才会要求您离开餐桌)

如果您是弱客户(类似于弱参考),那么服务员可以(在任何时间点)随意要求您离开餐厅:P

于 2018-12-15T05:16:33.027 回答
17

您使用的三个术语主要与 Object 是否有资格收集垃圾有关。

弱引用:: 它的引用强度不足以强制对象保留在内存中。它是垃圾收集器的一时兴起来收集该对象以进行垃圾收集。 你不能强迫那个 GC 不收集它

软引用::它或多或少与弱引用相同。但是你可以说它比垃圾回收中的弱引用更强烈地持有对象。

如果垃圾收集器在第一个生命周期本身收集弱引用,它将在下一个垃圾收集周期收集软引用。

强引用::它与上述两种引用正好相反。他们不太喜欢收集垃圾(大多数情况下它们从未被收集过。)

您可以参考以下链接了解更多信息:

http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/ref/Reference.html

于 2012-03-21T17:07:42.590 回答
11

4 度参考 -Strong, Weak, Soft, Phantom

强 - 是一种引用,它使被引用的对象不符合 GC 的条件。构建器类。例如 - StringBuilder

弱 - 是符合 GC 条件的参考。

Soft - 是一种引用,其对象在内存可用之前符合 GC 条件。最适合图像缓存。它将保留它们直到内存可用。

Phantom - 是一种引用,其对象直接符合 GC 条件。仅用于知道何时从内存中删除对象。

用途:

  1. 允许您识别对象何时从内存中完全删除。

  2. finalize()方法重载时,对于两个类的 GC 合格对象,可能不会及时发生 GC。所以幻像引用使它们之前有资格进行 GC ,这就是为什么即使大部分堆都是垃圾finalize()也可以获得OutOfMemoryErrors的原因。

弱引用是实现缓存模块的理想选择。

于 2015-07-29T17:07:00.903 回答
10

强引用

这些是我们每天编写的常规对象引用:

Employee emp = new Employee();

变量“emp”持有对 Employee 对象的强引用,并且可以通过任何强引用链访问的对象不符合垃圾回收条件。通常,这是您想要的,但并非总是如此。现在假设我们正在从集合或地图中的数据库中获取大量员工,并且我们需要定期对它们进行大量处理,因此为了保持性能,我们会将它们保存在缓存中。

就这样很好,但现在我们需要不同的数据,我们不需要那些 Employee 对象,除了缓存之外,这些对象不会从任何地方引用。哪个导致内存泄漏,因为这些对象没有被使用但仍然没有资格进行垃圾回收,并且我们无法从缓存中删除这些对象,因为我们没有对它们的引用?所以这里要么我们需要手动清空整个缓存,这很乏味,要么我们可以使用其他类型的引用,例如弱引用。

弱引用

弱引用不会将对象固定到内存中,如果没有从其他引用中引用,则将在下一个 GC 周期中进行 GC。我们可以使用 Java 提供的 WeakReference 类来创建上述类型的缓存,它不会存储没有从其他地方引用的对象。

WeakReference<Cache> cache = new WeakReference<Cache>(data);

要访问数据,您需要调用 cache.get()。如果弱引用被垃圾回收,则此 get 调用可能会返回 null:您必须检查返回的值以避免 NPE。Java 提供了使用弱引用的集合,例如,WeakHashMap 类将键(而非值)存储为弱引用。如果键是 GC'd 那么值也将自动从地图中删除。

由于弱引用也是对象,我们需要一种方法来清理它们(当它们引用的对象被 GC 时它们不再有用)。如果您将 ReferenceQueue 传递给弱引用的构造函数,那么垃圾收集器会在完成或 GC 之前将该弱引用附加到 ReferenceQueue。您可以定期处理此队列并处理死引用。

软引用

SoftReference 类似于 WeakReference,但它不太可能被垃圾收集。垃圾收集器根据内存需求自行决定清除软引用。虚拟机保证在抛出 OutOfMemoryError 之前,所有对软可访问对象的软引用都已被清除。

幻影参考

幻像引用是所有引用类型中最弱的,对它们调用 get 将始终返回 null。一个对象在它完成后被幻影引用,但在其分配的内存被回收之前,与弱引用相反,弱引用在完成之前入队或很少使用 GC 的幻影引用。

那么它们有什么用呢?当你构造一个幻像引用时,你必须总是传入一个 ReferenceQueue。这表明您可以使用幻像引用来查看您的对象何时被 GC'd。

嘿,所以如果弱引用在被认为是 finalize 但尚未 GC 时被排队,我们可以在 finalizer 块中创建一个对对象的新强引用并防止对象被 GC。是的,你可以,但你可能不应该这样做。为了检查这种情况,每个对象至少会发生两次 GC 循环,除非该对象只能通过幻像引用访问。这就是为什么即使你的内存包含大量垃圾,你也会用完堆。幻影引用可以防止这种情况。

您可以在我的文章Java 中的引用类型(强、软、弱、幻)中阅读更多内容。

于 2018-03-20T07:39:59.693 回答