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

use*_*262 33 python garbage-collection cpython

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

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

在使用gc模块之前,Python如何检测和释放循环内存引用?

Dav*_*ver 26

在使用gc模块之前,Python如何检测和释放循环内存引用?

它没有.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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

  • 顺便说一下,那个列表是[`gc.garbage`](http://docs.python.org/library/gc.html#gc.garbage). (3认同)
  • 从Python 3.4开始,收集带有`__del__`方法的循环.(PEP 442) (3认同)
  • @ user1245262,`__del__`问题与查找垃圾无关.Python确实发现这些对象是垃圾并将它们粘贴在`gc.garbage`列表中这些对象不被删除的唯一原因是python无法告诉删除它们的安全顺序是什么. (2认同)

kin*_*all 5

在使用gc模块之前,Python如何检测和释放循环内存引用?

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

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

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


use*_*262 5

我想我在@SvenMarnich提供的一些链接中找到了我正在寻找的答案:

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

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

*gc_ref*计数大于1的任何容器对象(?我认为0,但现在好吗?)的引用不是容器对象.因此它们是可达的,并且从考虑不可达的存储器岛中移除.

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

剩余的容器对象不可访问(彼此除外),应该被释放.

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