__del__
不再防止循环引用的收集,并且对于循环引用确实会被调用。 - Glenn Maynard稍微扩展一下Frédéric的回答,文档中的“引用计数”部分很好地解释了补充循环检测。
由于我发现解释事物是确认我理解的好方法,这里有一些例子... 假设有这两个类:
class WithDel(object):
def __del__(self):
print "deleting %s object at %s" % (self.__class__.__name__, id(self))
class NoDel(object):
pass
创建一个对象并从a
中失去引用,由于引用计数的原因会触发__del__
方法:
>>> a = WithDel()
>>> a = None # leaving the WithDel object with no references
deleting WithDel object at 4299615184
__del__
方法的对象之间创建一个引用循环,这次由于循环检测,所有内容仍然是无泄漏的。首先,启用垃圾收集调试输出:>>> import gc
>>> gc.set_debug(gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS)
然后在这两个对象之间创建一个引用循环:
>>> a = NoDel(); b = NoDel()
>>> a.other = b; b.other = a # cyclical reference
>>> a = None; b = None # Leave only the reference-cycle
>>> gc.collect()
gc: collectable <NoDel 0x10046ed50>
gc: collectable <NoDel 0x10046ed90>
gc: collectable <dict 0x100376c20>
gc: collectable <dict 0x100376b00>
4
>>> gc.garbage
[]
>>> a = NoDel(); b = WithDel()
>>> a.other = b; b.other = a
>>> a = None; b = None
>>> gc.collect()
gc: uncollectable <WithDel 0x10046edd0>
gc: uncollectable <dict 0x100376b00>
gc: uncollectable <NoDel 0x10046ed90>
gc: uncollectable <dict 0x100376c20>
4
>>> gc.garbage
[<__main__.WithDel object at 0x10046edd0>]
正如Paul所提到的,循环可以使用weakref
打破:
>>> import weakref
>>> a = NoDel(); b = WithDel()
>>> a.other = weakref.ref(b)
>>> b.other = a # could also be a weakref
当对 WithDel
对象的引用 b
丢失时,即使存在循环引用,该对象也会被删除:
>>> b = None
deleting WithDel object at 4299656848
>>> a.other
<weakref at 0x10045b9f0; dead>
哦,objgraph会有帮助地像这样指出有问题的__del__
方法
Python的垃圾回收机制旨在遍历所有活动对象,以查找并消除没有外部引用的引用循环。
您可以通过运行gc.collect()
,然后打印gc.garbage
和gc.get_objects来验证正在发生的情况。