我试图了解 Python 的垃圾收集器如何检测循环引用。当我查看文档时,我看到的只是一个声明,即检测到循环引用,除非涉及的对象有__del__
方法。
如果发生这种情况,我的理解(可能是错误的)是 gc 模块通过(我假设)遍历所有分配的内存并释放任何无法访问的块来充当故障保护。
在使用 gc 模块之前,Python 如何检测和释放循环内存引用?
我试图了解 Python 的垃圾收集器如何检测循环引用。当我查看文档时,我看到的只是一个声明,即检测到循环引用,除非涉及的对象有__del__
方法。
如果发生这种情况,我的理解(可能是错误的)是 gc 模块通过(我假设)遍历所有分配的内存并释放任何无法访问的块来充当故障保护。
在使用 gc 模块之前,Python 如何检测和释放循环内存引用?
在使用 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_traverse
是list_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
),以便程序员可以手动“处理”它们。
在使用 gc 模块之前,Python 如何检测和释放循环内存引用?
Python 的垃圾收集器(实际上不是gc
模块,它只是垃圾收集器的 Python 接口)执行此操作。因此,Python在使用垃圾收集器之前不会检测和释放循环内存引用。
Python 通常会在引用计数达到零时立即释放大多数对象。(我说“大多数”是因为它从不释放例如小整数或内部字符串。)在循环引用的情况下,这永远不会发生,因此垃圾收集器会定期遍历内存并释放循环引用的对象。
当然,这都是 CPython 特有的。其他 Python 实现有不同的内存管理(Jython = Java VM,IronPython = Microsoft .NET CLR)。
我想我在@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是源链接代码,其中有注释进一步解释了循环引用检测背后的想法