假设我有一段维护父/子结构的代码。在这样的结构中,我会遇到循环引用,其中一个子指向一个父亲,而一个父亲也指向一个孩子。我应该担心它们吗?我正在使用Python 2.5。
我担心它们不会被垃圾收集并且应用程序最终会消耗所有内存。
假设我有一段维护父/子结构的代码。在这样的结构中,我会遇到循环引用,其中一个子指向一个父亲,而一个父亲也指向一个孩子。我应该担心它们吗?我正在使用Python 2.5。
我担心它们不会被垃圾收集并且应用程序最终会消耗所有内存。
“担忧”是不必要的,但如果你的程序变得比预期更慢、消耗的内存超出预期或出现奇怪的暂停,原因确实可能在那些垃圾引用循环中——它们需要通过一种不同于“常规”(非循环)引用图的垃圾回收过程进行回收,并且如果你有大量对象被绑定在这样的循环中,那么该回收可能较慢(如果循环中的一个对象具有`__del__`特殊方法,则也会禁止循环垃圾回收)。
因此,引用循环不会影响程序的正确性,但可能会影响其性能和/或占用空间。
如果您想要删除不需要的引用循环,通常可以使用Python标准库中的weakref模块。
如果您想要更直接地控制(或执行调试,查看发生了什么),关于循环垃圾回收,请使用Python标准库中的gc模块。
实验结果表明:你没问题:
import itertools
for i in itertools.count():
a = {}
b = {"a":a}
a["b"] = b
它始终保持在使用3.6 MB的RAM。
当没有外部引用时,Python会检测到循环引用并释放内存。
__del__
方法。通常不应该有,但你永远不知道。有一段时间,即使是collections.OrderedDict
也有一个这样的方法,原因不明。 - Antimony循环引用是一种常见的做法,因此我认为没有必要担心它们。许多树算法要求每个节点都有指向其子节点和父节点的链接。它们还需要实现类似于双向链表的东西。
在变量中的列表中引用方法似乎存在问题。以下是两个示例。第一个示例不调用__del__
。使用weakref的第二个示例可以调用__del__
,但在这种情况下问题是您无法弱引用方法:http://docs.python.org/2/library/weakref.html
import sys, weakref
class One():
def __init__(self):
self.counters = [ self.count ]
def __del__(self):
print("__del__ called")
def count(self):
print(sys.getrefcount(self))
sys.getrefcount(One)
one = One()
sys.getrefcount(One)
del one
sys.getrefcount(One)
class Two():
def __init__(self):
self.counters = [ weakref.ref(self.count) ]
def __del__(self):
print("__del__ called")
def count(self):
print(sys.getrefcount(self))
sys.getrefcount(Two)
two = Two()
sys.getrefcount(Two)
del two
sys.getrefcount(Two)
我不认为你应该担心。尝试以下程序,你会发现它不会消耗所有的内存:
while True:
a=range(100)
b=range(100)
a.append(b)
b.append(a)
a.append(a)
b.append(b)
a.extend(b)
而不是 append
? - richizy
__del__
的注释需要加上。如果你的对象析构函数有副作用,那么你可能需要更仔细地考虑循环引用(以及何时销毁)。 - speedplane