我试图理解Python的垃圾收集器如何检测循环引用。当我查看文档时,我看到的只是一个声明,即检测到循环引用,除非涉及的对象具有__del__
方法。
如果发生这种情况,我的理解(可能是错误的)是gc模块通过(我假设)遍历所有分配的内存并释放任何无法访问的块来充当故障保护。
Python如何检测&在使用gc模块之前自由循环内存引用?
答案 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_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
访问),以便程序员可以手动“处理”它们。
答案 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)。