问题 垃圾收集 - 孤立的LinkedList链接


假设你有参考 A -> B -> C -> D。删除引用时 B 从 A,你留下了一个孤立的物体链 B -> C -> D

C 和 D 即使没有办法到达他们也是垃圾收集(因为没有参考 B)?

我认为GC对此很聪明,并将解决任何此类依赖关系。

但是,我看了一下 源代码 为了 LinkedList 上课并发现了与这种信仰相悖的东西。我注意到列表是 clear()ed,对每个链接的所有引用都明确设置为 null从而使它成为一个 O(n) 操作。这样做有什么理由/好处吗?


8164
2017-08-04 02:36


起源



答案:


这确实看起来有点奇怪。也许明确拆解列表的原因是清除现有迭代器和子列表以及父列表的列表。

肯定没有做更快的垃圾收集。垃圾收集器不会遍历无法访问的对象中的引用,因此将它们置零将不会产生任何差别。

UPDATE

该方法的更新版本具有以下注释:

// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
//   more than one generation
// - is sure to free memory even if there is a reachable Iterator

因此,至少在某些情况下,似乎GC有一个好处。

假设a Node 在老一代中包含对对象的引用(例如 Node 或年轻一代的元素。当收集年轻一代时,该参考成为“根”,导致年轻一代被保留,即使是老一代 Node 无法到达。这种状态持续到收集老一代。老一代很少被收集。

如果遍历列表并将其拆除,则会为包含旧 - >新引用的变量分配一个 null。该分配的写屏障导致(立即或在GC时间)原始引用不再是“根”。因此,现在可以收集年轻一代中的对象,并且它不会最终“老化”给老一代(这提出了需要收集该代的时间)。

据推测,GC的好处超过了取消列表的成本......平均而言,或者在成本是灾难性的情况下。

有关更多信息,请参阅Jones和Lins的“用于动态内存管理的垃圾收集算法”。我的(第一版)副本见第7.5章。


一般来说,扔一个更好 Collection 离开并重新开始,而不是将其清除以便重复使用。


9
2017-08-04 04:33



啊,是的,这是完全正确的! Iterators和 ListIterators都包含对内部节点的引用。 SubList然而,不。 - tskuzzy
为了记录,源现在明确地证明它可以使世代GC受益。 - shmosel
@shmosel那 什么 可以使世代GC受益吗? - Kevin Krumwiede
@KevinKrumwiede我们正在讨论什么......拆解 LinkedList。 - shmosel
@shmosel要明确的是,你是否反驳或支持最好将整个清单扔掉的说法?我看了看 LinkedList GrepCode上的源代码并没有找到你所指的内容。 (我知道,这个帖子很老了...) - Kevin Krumwiede


答案:


这确实看起来有点奇怪。也许明确拆解列表的原因是清除现有迭代器和子列表以及父列表的列表。

肯定没有做更快的垃圾收集。垃圾收集器不会遍历无法访问的对象中的引用,因此将它们置零将不会产生任何差别。

UPDATE

该方法的更新版本具有以下注释:

// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
//   more than one generation
// - is sure to free memory even if there is a reachable Iterator

因此,至少在某些情况下,似乎GC有一个好处。

假设a Node 在老一代中包含对对象的引用(例如 Node 或年轻一代的元素。当收集年轻一代时,该参考成为“根”,导致年轻一代被保留,即使是老一代 Node 无法到达。这种状态持续到收集老一代。老一代很少被收集。

如果遍历列表并将其拆除,则会为包含旧 - >新引用的变量分配一个 null。该分配的写屏障导致(立即或在GC时间)原始引用不再是“根”。因此,现在可以收集年轻一代中的对象,并且它不会最终“老化”给老一代(这提出了需要收集该代的时间)。

据推测,GC的好处超过了取消列表的成本......平均而言,或者在成本是灾难性的情况下。

有关更多信息,请参阅Jones和Lins的“用于动态内存管理的垃圾收集算法”。我的(第一版)副本见第7.5章。


一般来说,扔一个更好 Collection 离开并重新开始,而不是将其清除以便重复使用。


9
2017-08-04 04:33



啊,是的,这是完全正确的! Iterators和 ListIterators都包含对内部节点的引用。 SubList然而,不。 - tskuzzy
为了记录,源现在明确地证明它可以使世代GC受益。 - shmosel
@shmosel那 什么 可以使世代GC受益吗? - Kevin Krumwiede
@KevinKrumwiede我们正在讨论什么......拆解 LinkedList。 - shmosel
@shmosel要明确的是,你是否反驳或支持最好将整个清单扔掉的说法?我看了看 LinkedList GrepCode上的源代码并没有找到你所指的内容。 (我知道,这个帖子很老了...) - Kevin Krumwiede


是的,C和D将被垃圾收集,假设B是唯一引用它们的东西。这是因为它们无法从图形到应用程序对象图的根对象。

我想象标记每个链接的原因 null 在里面 LinkedList 实现是为了防止内存泄漏。它可能是在外面的东西 LinkedList抓住头节点。如果发生这种情况,它将使所有其他节点保持活着,即使在之后 LinkedList 已被清除。


2
2017-08-04 02:45



也, null它们可以让垃圾收集器更有效地清除它们,而无需走动对象图,对吗? - OverZealous
触发垃圾收集时,将遍历整个对象图,并将对象标记为“正在使用中”。所以,是的,我想它会缩短遍历时间。 - nicholas.hauschild
实际上,典型的垃圾收集器只遍历可到达的对象图。不会遍历从B到C和从C到D的链接,因此将它们归零不会有助于提高性能。 - Stephen C
@Stephen C:对不起,我不是很清楚。我在谈论可达到的对象图。我也假设将以前可达的值归零将使遍历更短。我想我今天只是误解了所有人...... - nicholas.hauschild
这是真的,除非没有办法获得头节点的外部引用 private 它没有吸气剂。 - tskuzzy