Python的垃圾收集器如何检测循环引用?

时间:2012-06-09 15:52:17

标签: python garbage-collection cpython

我试图理解Python的垃圾收集器如何检测循环引用。当我查看文档时,我看到的只是一个声明,即检测到循环引用,除非涉及的对象具有__del__方法。

如果发生这种情况,我的理解(可能是错误的)是gc模块通过(我假设)遍历所有分配的内存并释放任何无法访问的块来充当故障保护。

Python如何检测&在使用gc模块之前自由循环内存引用?

3 个答案:

答案 0 :(得分:24)

  

Python如何检测&在使用gc模块之前自由循环内存引用?

没有。 gc仅存在检测和释放循环引用。非循环引用通过引用计数来处理。

现在,要查看 gc如何确定任何给定对象引用的对象集,请查看gc_get_references中的Modules/gcmodule.c函数。相关的一点是:

// Where `obj` is the object who's references we want to find
traverseproc traverse;
if (! PyObject_IS_GC(obj))
    continue;
traverse = Py_TYPE(obj)->tp_traverse;
if (! traverse)
    continue;
if (traverse(obj, (visitproc)referentsvisit, result)) {
    Py_DECREF(result);
    return NULL;
}

这里的主要功能是tp_traverse。每个C级类型定义tp_traverse函数(或者对于不包含任何引用的对象,如str,将其设置为NULL)。 tp_traverse的一个示例是list_traverselist的遍历函数:

static int
list_traverse(PyListObject *o, visitproc visit, void *arg)
{
    Py_ssize_t i;

    for (i = Py_SIZE(o); --i >= 0; )
        Py_VISIT(o->ob_item[i]);
    return 0;
}
  

我看到的是一个声明,即检测到循环引用,除非涉及的对象具有__del__()方法。

你是对的 - Python的循环检测器可以检测和收集循环,除非它们包含带有__del__方法的对象,因为解释器无法安全地删除这些对象(到得到一个直觉,为什么会这样,想象你有两个对象用__del__方法相互引用。它们应该以哪种顺序被释放?)。

当循环中涉及具有__del__方法的对象时,垃圾收集器会将它们粘贴在单独的列表中(可通过gc.garbage访问),以便程序员可以手动“处理”它们。

答案 1 :(得分:5)

我想我在@SvenMarnich提供的一些链接中找到了我正在寻找的答案,对原始问题的评论:

Container对象是可以保存对其他Python对象的引用的Python对象。列表,类,元组等是容器对象;整数,字符串等不是。因此,只有容器对象存在循环引用的风险。

每个Python对象都有一个字段 - * gc_ref *,对于非容器对象,它(我相信)设置为NULL。对于容器对象,它设置为等于引用它的非容器对象的数量

* gc_ref * count大于1的任何容器对象(?我认为0,但现在好吗?)的引用不是容器对象。所以它们是可以访问的,并且从考虑无法访问的内存岛中被删除。

任何已知可达的对象(即我们刚刚识别为* gc_ref * count大于1的对象)可以访问的容器对象也不需要被释放。

其他容器对象无法访问(彼此除外),应该被释放。

http://www.arctrix.com/nas/python/gc/是一个提供更全面解释的链接 http://hg.python.org/cpython/file/2059910e7d76/Modules/gcmodule.c是源代码的链接,其中的评论进一步解释了循环参考检测背后的想法

答案 2 :(得分:4)

  

Python如何检测&在使用gc模块之前自由循环内存引用?

Python的垃圾收集器(实际上不是gc模块,它只是垃圾收集器的Python接口)这样做。因此,Python 在使用垃圾收集器之前不会检测并释放循环内存引用。

Python通常会在引用计数达到零时立即释放大多数对象。 (我说“大多数”因为它永远不会释放,例如,小整数或实习字符串。)在循环引用的情况下,这种情况永远不会发生,因此垃圾收集器会定期遍历内存并释放循环引用的对象。

当然,这是所有CPython特有的。其他Python实现具有不同的内存管理(Jython = Java VM,IronPython = Microsoft .NET CLR)。