Python GC处理这样的引用循环吗?

2024-05-13 10:37:14 发布

您现在位置:Python中文网/ 问答频道 /正文

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

InstanceState loop

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

环的视野稍宽:

Wider view of InstanceState loop


Tags: 对象objgraph垃圾收集器视野
3条回答

为了对Frédéric的答案进行一些扩展,文档中的the "reference counts" section很好地解释了补充循环检测。

既然我觉得解释是确认我理解的好方法,这里有一些例子。。。对于这两个类:

class WithDel(object):
    def __del__(self):
        print "deleting %s object at %s" % (self.__class__.__name__, id(self))


class NoDel(object):
    pass

创建对象并丢失来自a的引用将触发__del__方法,这要归功于ref计数:

>>> a = WithDel()
>>> a = None  # leaving the WithDel object with no references 
deleting WithDel object at 4299615184

如果我们用no__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>]

正如Paul提到的,循环可以用^{}来中断:

>>> 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会有帮助的indicated the problematic ^{} method like this

Python的标准引用计数机制无法释放周期,因此示例中的结构将泄漏。

但是,supplemental garbage collection facility在默认情况下是启用的,如果它的任何组件都不能从外部访问,并且它们没有__del__()方法,则应该能够释放该结构。

如果是,垃圾收集器will not free them因为它无法确定运行这些__del__()方法的安全顺序。

Python的GC被设计为遍历所有活动对象,以便在没有外部引用的情况下定位和消除引用周期。

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

相关问题 更多 >