假设您有引用A -> B -> C -> D
。当您从A
中删除对B
的引用时,您将留下一个孤立的对象链B -> C -> D
。
C
和D
将被垃圾收集,即使没有办法得到他们(因为没有引用B
)?
我想GC很聪明,会解决任何这样的依赖关系。
然而,我看了看LinkedList
类的源代码,发现了一些与这种信念相反的东西。我注意到,当列表被clear()
编辑时,对每个链接的所有引用都显式设置为null
,从而使其成为O(n)
操作。这样做有什么理由/好处吗?
这看起来确实有点奇怪。可能它显式地拆除列表的原因是为了为现有的迭代器和子列表以及父列表清除列表。
这样做当然不是为了使垃圾收集更快。垃圾收集器不会遍历不可达对象中的引用,因此为空它们不会产生任何影响。
该方法的最新版本有如下注释:
// 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有好处。
假设老一代中的Node
包含对年轻一代中的对象(例如Node
或元素)的引用。在收集年轻代时,该引用成为"根",导致年轻代对象被保留,即使老代Node
不可达。这种状态一直持续到旧的代被收集。老代很少被收集。
如果遍历列表并拆除它,则包含old -> new引用的变量被赋值为null
。该赋值的写屏障导致(立即或在GC时)原始引用不再是"根"。因此,现在可以收集年轻代中的对象,并且它不会最终"保留"到较老的代(这会使需要收集该代的时间提前)。
可以推测,GC的好处超过了解取列表的成本…无论是在平均情况下,还是在成本是灾难性的情况下。
有关更多信息,请参阅Jones和Lins的"动态内存管理的垃圾收集算法"。它在我的(第一版)版本的第7.5章。
一般来说,丢弃Collection
对象并重新开始比清除它以供重用要好。
是的,C和D将被垃圾收集,假设B是唯一引用它们的东西。这是因为从图到应用程序对象图的根对象是不可达的。
我认为在LinkedList
实现中将每个链接标记为null
的原因是为了防止内存泄漏。LinkedList
之外的东西有可能抓住头部节点。如果发生这种情况,即使在清除LinkedList
之后,它也会使所有其他节点保持活动状态。