为什么命名元组总是被python跟踪';s GC?
正如我们(或至少我)在简单元组中了解到的,python的垃圾收集器不会跟踪只包含不可变值的元组,一旦它发现它们永远不能参与引用循环:为什么命名元组总是被python跟踪';s GC?,python,garbage-collection,Python,Garbage Collection,正如我们(或至少我)在简单元组中了解到的,python的垃圾收集器不会跟踪只包含不可变值的元组,一旦它发现它们永远不能参与引用循环: >>> import gc >>> x = (1, 2) >>> gc.is_tracked(x) True >>> gc.collect() 0 >>> gc.is_tracked(x) False s为什么不是这样,它是集合模块中元组的一个子类,具有命名字段 >&
>>> import gc
>>> x = (1, 2)
>>> gc.is_tracked(x)
True
>>> gc.collect()
0
>>> gc.is_tracked(x)
False
s为什么不是这样,它是集合模块中元组的一个子类,具有命名字段
>>> import gc
>>> from collections import namedtuple
>>> foo = namedtuple('foo', ['x', 'y'])
>>> x = foo(1, 2)
>>> gc.is_tracked(x)
True
>>> gc.collect()
0
>>> gc.is_tracked(x)
True
他们的实现中是否有某种固有的东西阻止了这一点,或者只是被忽略了?我能找到的关于这一点的唯一评论是在Python源代码的
gcmodule.c
文件中:
注意:关于取消跟踪可变对象。
某些类型的容器不能参与引用循环,因此不需要垃圾收集器进行跟踪。
取消跟踪这些对象可以降低垃圾收集的成本。
但是,确定哪些对象可能未被跟踪并不是自由的,
而且成本必须与垃圾带来的好处相权衡
收藏
对于何时取消跟踪容器,有两种可能的策略:
is_tracked(obj)
,该函数
返回对象的当前跟踪状态。后来的
垃圾回收可能会更改对象的跟踪状态。
问题中引入了对某些容器的取消跟踪,并针对问题改进了算法
(请参阅链接问题以查看为允许取消跟踪而引入的真实代码)
这个注释有点模棱两可,但是它没有说明选择“取消跟踪”哪个对象的算法适用于泛型容器。这意味着代码只检查tuple
s(和dict
s),而不是它们的子类
您可以在文件的代码中看到:
/* Try to untrack all currently tracked dictionaries */
static void
untrack_dicts(PyGC_Head *head)
{
PyGC_Head *next, *gc = head->gc.gc_next;
while (gc != head) {
PyObject *op = FROM_GC(gc);
next = gc->gc.gc_next;
if (PyDict_CheckExact(op))
_PyDict_MaybeUntrack(op);
gc = next;
}
}
注意调用PyDict\u CheckExact,以及:
static void
move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
{
PyGC_Head *gc = young->gc.gc_next;
/* omissis */
if (PyTuple_CheckExact(op)) {
_PyTuple_MaybeUntrack(op);
}
注意调用PyTuple\u CheckExact
还要注意的是,tuple
的子类不一定是不变的。这意味着,如果您想在tuple
和dict
之外扩展此机制,您需要一个通用的是不可变的
函数。如果可能的话,这将非常昂贵,因为Python的动态性(例如,类的方法可能会在运行时更改,而对于tuple
,这是不可能的,因为它是内置类型)。因此,开发人员选择只使用一些著名的内置组件,而不使用一些特殊情况
这就是说,我相信它们也可以将特例
命名为tuple
s,因为它们是非常简单的类。例如,当您调用namedtuple
创建一个新类时,会出现一些问题,因此GC应该检查子类。
这可能是类似以下代码的问题:
class MyTuple(namedtuple('A', 'a b')):
# whatever code you want
pass
因为MyTuple
类不需要是不可变的,所以GC应该检查该类是否是namedtuple
的直接子类以确保安全。不过,我很确定这种情况下有解决办法
它们可能没有,因为namedtuple
s是标准库的一部分,而不是python核心。也许开发者不想让内核依赖于标准库的模块
所以,要回答你的问题:
- 不,在它们的实现中,没有任何东西可以固有地阻止对
s的取消跟踪namedtuple
- 不,我相信他们并没有“忽视”这一点。然而,只有python开发人员才能清楚地回答为什么他们选择不包含它们。我的猜测是,他们认为这不会为更改提供足够大的好处,他们不想让核心依赖于标准库
- @Bakunu给出了一个极好的答案——接受它:——)
这里有一个亮点:没有一个未跟踪的噱头是“免费的”:在运行时和需要维护的复杂代码的爆炸性增长方面都有实际的成本。用户程序和CPython实现都大量使用基本元组和dict类型,而且通常可以取消对它们的跟踪。因此,对它们进行特殊的屏蔽是值得付出一些痛苦的,而且对“几乎所有”项目都有好处。虽然可以找到一些程序示例,这些程序也可以从取消跟踪
namedtuple
s(或…)中获益,但这对CPython实现或大多数用户程序都没有好处。但这会给所有程序带来成本(gc代码中有更多的条件询问“这是一个命名的双倍吗
?”,等等)
请注意,所有容器对象都受益于CPython的“分代”循环gc技巧:给定容器保存的集合越多,扫描该容器的频率就越低(因为容器被移动到“老一代”,即