__del__
方法,否则会检测到循环引用。如果发生这种情况,我了解到(可能不准确)gc模块会充当故障保护措施,通过(我假设)遍历所有已分配的内存并释放任何不可访问的块来释放内存。
Python如何在使用gc模块之前检测和释放循环引用的内存呢?
__del__
方法,否则会检测到循环引用。Modules/gcmodule.c
中的gc_get_references
函数。相关部分如下:// 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
访问),以便程序员可以手动“处理”它们。
__del__
问题实际上与找到垃圾不太相关。Python确实会发现这些对象是垃圾并将它们粘贴到gc.garbage
列表中。之所以不删除这些对象的唯一原因是Python不能确定安全删除它们的顺序。 - Winston Ewert__del__
方法的对象时,循环检测器可以回收所有东西。然而,由于循环检测器不能安全地收集存在__del__
方法的对象,因此涉及这些对象的循环通过gc
模块暴露出来,允许程序员手动清理它们”。 - David Wolever__del__
方法的循环引用对象将被回收。(PEP 442) - Antimonygc
模块 就是 循环垃圾收集器。它不仅仅是 Python 接口,而且是实现。 - Sven Marnachgc
模块不在sys.modules
中,而一些其他模块的实现是内置于Python中的,因此我认为gc
模块只是实现。感谢更新。 - kindall我认为在原问题的评论中,@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是源代码的链接,其中有进一步解释循环引用检测思路的评论。