15

使用objgraph,我发现了一堆这样的对象:

实例状态循环

Python 的垃圾收集器会处理这样的循环,还是会泄漏?

循环的略宽视图:

更广泛的 InstanceState 循环视图

4

4 回答 4

26

Python 的标准引用计数机制无法释放循环,因此您示例中的结构会泄漏。

然而,补充垃圾收集设施默认启用并且应该能够释放该结构,如果其组件不再可以从外部访问并且它们没有__del__()方法

如果它们这样做了,垃圾收集器不会释放它们,因为它无法确定运行这些__del__()方法的安全顺序。

于 2011-11-06T08:19:07.157 回答
19

为了稍微扩展 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
[]

dict来自对象内部__dict__属性)

一切都很好,直到循环中的一个对象包含一个__del__方法:

>>> 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>]

正如保罗所提到的,循环可以被打破weakref

>>> import weakref
>>> a = NoDel(); b = WithDel()
>>> a.other = weakref.ref(b)
>>> b.other = a # could also be a weakref

然后,当对对象的b引用WithDel丢失时,它会被删除,尽管循环:

>>> b = None
deleting WithDel object at 4299656848
>>> a.other
<weakref at 0x10045b9f0; dead>

哦,objgraph会很有帮助地指出像这样有问题的__del__方法

于 2011-11-06T14:27:54.040 回答
5

Python 的 GC 旨在遍历所有活动对象以定位和消除没有外部引用的引用循环。

gc.collect()您可以通过运行然后打印gc.garbagegc.get_objects来验证正在发生的事情。

于 2011-11-06T08:17:15.487 回答
2

如果您对父指针使用弱引用,则 GC 将正常发生。

于 2011-11-06T08:28:21.540 回答