5

背景

我有一个树结构。在这个树结构中,我将一个节点的孩子维护为一个双向链表:

在此处输入图像描述
(来源:双向链表

(由于创建此列表的广度优先搜索方法,我选择了此结构。)

问题

现在我担心的是垃圾收集器是否可以自动销毁这个列表。自然我只保留对这三个根节点的引用。Afaik GC 的原理是它收集内存中的数据结构,不指向任何引用。但是在双向链表中,每个节点都是从它的兄弟节点引用的,而兄弟节点引用该节点。所以总会有一个节点的引用,而 GC 永远不会收集它。

垃圾收集器会处理双向链表吗?

如果没有,最简单的收集方法是什么?

相关问题:

为什么 Lua 使用垃圾收集器而不是引用计数?
Python:修改列表时的内存使用和优化

4

1 回答 1

9

每个 Python 实现都有不同的垃圾收集方案。通用答案是“是的,如果是垃圾,就应该进行垃圾收集”。但是您可能想要比这更具体的东西。


在 CPython 中,垃圾收集使用引用计数和循环收集器。如果一个对象的引用计数下降到 0,它就会被清除。但是在您的情况下,当对您的列表的所有外部引用都消失时,仍然会有内部引用,因此引用计数本身无法解决您的问题。这就是循环收集器的用途。

假设您的节点没有__del__方法,并且您没有(直接或间接)禁用“补充垃圾收集”(默认情况下启用),循环收集器将检测到您的节点都相互引用,但没有其他引用它们, 并将其清理干净。(这可能需要两次,因为它使用了代系统。)

您可以使用该gc模块显式运行循环收集器 ( gc.collect()) 而不是等待它,或者检查它在做什么。例如,如果您这样做:

gc.collect()
oldcounts = gc.get_counts()
del last_reference_to_list
gc.collect()
newcounts = gc.get_counts()
print(oldcounts, newcounts)

......你应该能够告诉(不是完全可靠,但足以用于学习和测试目的)你的节点都已经消失了。


如果您的节点确实__del__方法怎么办?然后你将不得不给 GC 一些帮助。您需要做的是打破包含带有__del__方法的对象的任何循环。如果列表之间没有任何节点共享,那么明显的方法就是遍历列表以及del前向和后向指针。(从技术上讲,你只需要del一个或另一个,但你也可以两者都做。)如果你需要__del__节点上的方法,你可能需要顶层的方法dl_list(或tree_node拥有这些的任何东西),所以这是一个很明显的地方。

当然,如果您不需要该__del__方法,还有一个更简单的解决方案:摆脱它。


最后一种可能性是weakref用于反向链接,但常规引用用于正向链接。这样,就没有可能的循环。但是你必须小心地添加和删除节点,以确保你永远不会暂时离开一个节点,除了一个弱引用之外什么都没有。


如果您使用的是 Jython 或 IronPython,垃圾收集与底层运行时(JVM 或 .NET)相关联,因此您必须阅读相应的文档。

PyPy 有自己的垃圾收集器(实际上,可以选择不同的选项),您可以在此处阅读。

如果您使用的是不太常见的实现,则应该有类似的文档可用。

于 2013-08-05T22:08:56.080 回答