Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么命名元组总是被python跟踪';s GC?_Python_Garbage Collection - Fatal编程技术网

为什么命名元组总是被python跟踪';s GC?

为什么命名元组总是被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为什么不是这样,它是集合模块中元组的一个子类,具有命名字段 >&

正如我们(或至少我)在简单元组中了解到的,python的垃圾收集器不会跟踪只包含不可变值的元组,一旦它发现它们永远不能参与引用循环:

>>> 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
文件中:

注意:关于取消跟踪可变对象。 某些类型的容器不能参与引用循环,因此不需要垃圾收集器进行跟踪。 取消跟踪这些对象可以降低垃圾收集的成本。 但是,确定哪些对象可能未被跟踪并不是自由的, 而且成本必须与垃圾带来的好处相权衡 收藏

对于何时取消跟踪容器,有两种可能的策略:

  • 创建容器时
  • 当垃圾收集器检查容器时
  • 仅包含不可变对象(整数、字符串等)的元组, 递归地,不可变对象的元组)不需要 跟踪。解释器创建大量元组,其中许多是 在垃圾收集之前,它将无法生存。因此 不值得在创建时取消跟踪符合条件的元组

    相反,除了空元组之外的所有元组都会在 创建。在垃圾收集过程中,确定是否存在 幸存的元组可以不被追踪。如果所有元组 其内容中的一部分已被跟踪。检查元组是否存在错误 在所有垃圾收集周期中取消跟踪。这可能需要超过 取消跟踪元组的一个周期

    仅包含不可变对象的词典也不需要 被跟踪。词典创建时不受跟踪。如果有人跟踪 项插入到字典中(作为键或值),则 词典被跟踪。在完全垃圾收集期间(所有 代),收集器将取消对其 不跟踪内容

    该模块提供python函数
    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核心。也许开发者不想让内核依赖于标准库的模块

    所以,要回答你的问题:

    • 不,在它们的实现中,没有任何东西可以固有地阻止对
      namedtuple
      s的取消跟踪
    • 不,我相信他们并没有“忽视”这一点。然而,只有python开发人员才能清楚地回答为什么他们选择不包含它们。我的猜测是,他们认为这不会为更改提供足够大的好处,他们不想让核心依赖于标准库

      • @Bakunu给出了一个极好的答案——接受它:——)

        这里有一个亮点:没有一个未跟踪的噱头是“免费的”:在运行时和需要维护的复杂代码的爆炸性增长方面都有实际的成本。用户程序和CPython实现都大量使用基本元组和dict类型,而且通常可以取消对它们的跟踪。因此,对它们进行特殊的屏蔽是值得付出一些痛苦的,而且对“几乎所有”项目都有好处。虽然可以找到一些程序示例,这些程序也可以从取消跟踪
        namedtuple
        s(或…)中获益,但这对CPython实现或大多数用户程序都没有好处。但这会给所有程序带来成本(gc代码中有更多的条件询问“这是一个
        命名的双倍吗
        ?”,等等)

        请注意,所有容器对象都受益于CPython的“分代”循环gc技巧:给定容器保存的集合越多,扫描该容器的频率就越低(因为容器被移动到“老一代”,即