Python GC是否处理这样的参考周期?

dbr*_*dbr 14 python garbage-collection memory-leaks objgraph

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

InstanceState循环

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

稍微宽一点的循环视图:

更广泛的InstanceState循环视图

Fré*_*idi 25

Python的标准引用计数机制不能释放循环,因此示例中的结构会泄漏.

补充的垃圾收集设施,但是,在默认情况下启用,并且应该能够释放该结构,如果没有它的组件可达从外面了,他们没有__del__()方法.

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

  • 应该明确说明,在Python中默认启用您称为"补充"的GC (5认同)
  • 华友世纪!至少,人们(你和Raymond Hettinger)不会混淆GC和refs计数器,并且不使用"垃圾收集器"作为"引用计数机制"的同义词!很长一段时间我一直困惑于这样一个事实,即我已经理解了这样的事情,但我不断阅读文本,其中人们表达了他们的想法,好像GC是杀死没有更多参考对象的责任.在你的答案中,差异是完美的表达和清晰.谢谢.因此+1 (3认同)

dbr*_*dbr 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
Run Code Online (Sandbox Code Playgroud)

由于引用计数,创建一个对象并丢失引用a会触发该__del__方法:

>>> a = WithDel()
>>> a = None  # leaving the WithDel object with no references 
deleting WithDel object at 4299615184
Run Code Online (Sandbox Code Playgroud)

如果我们在没有 __del__方法的情况下在两个对象之间进行参考循环,那么这一切仍然是泄漏的,这次归功于循环检测.首先,启用垃圾收集调试输出:

>>> import gc
>>> gc.set_debug(gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS)
Run Code Online (Sandbox Code Playgroud)

然后在两个对象之间建立一个引用循环:

>>> 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
[]
Run Code Online (Sandbox Code Playgroud)

(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>]
Run Code Online (Sandbox Code Playgroud)

正如保罗所说,循环可以用以下方式打破weakref:

>>> import weakref
>>> a = NoDel(); b = WithDel()
>>> a.other = weakref.ref(b)
>>> b.other = a # could also be a weakref
Run Code Online (Sandbox Code Playgroud)

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

>>> b = None
deleting WithDel object at 4299656848
>>> a.other
<weakref at 0x10045b9f0; dead>
Run Code Online (Sandbox Code Playgroud)

哦,objgraph会有用地指出这样有问题的__del__方法

  • @kralyk我猜这个pep是负责的:https://www.python.org/dev/peps/pep-0442/ (2认同)

Ray*_*ger 5

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

您可以通过运行gc.collect()然后打印gc.garbagegc.get_objects来验证发生了什么.