40

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

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

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

4

3 回答 3

34

在使用 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;
}

这里的主要功能是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;
}

我看到的是检测到循环引用的语句,除非涉及的对象有__del__()方法。

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

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

于 2012-06-09T16:05:59.650 回答
7

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

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

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

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

于 2012-06-09T16:06:32.320 回答
7

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

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

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

任何 *gc_ref* 计数大于 1 的容器对象(?我原以为是 0,但现在还可以吗?)具有不是容器对象的引用。因此它们是可访问的,并且不考虑成为不可访问的内存岛。

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

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

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

于 2012-06-09T17:38:16.493 回答