代码之家  ›  专栏  ›  技术社区  ›  Daniel Goldfarb

python capi引用计数详细信息

  •  1
  • Daniel Goldfarb  · 技术社区  · 7 年前

    我在看一些示例代码( https://docs.python.org/2.0/api/refcountDetails.html )试图更好地理解两个例子之间的区别:第一个例子是:

    PyObject *t;
    
    t = PyTuple_New(3);
    PyTuple_SetItem(t, 0, PyInt_FromLong(1L));
    PyTuple_SetItem(t, 1, PyInt_FromLong(2L));
    PyTuple_SetItem(t, 2, PyString_FromString("three"));
    

    作者解释了pytuple_setitem() 窃取参考资料 (所以没有必要去贬低它)。 好吧,我明白了 是的。然后,作者使用pysequence\u setitem()给出了类似的代码,其中 不偷参考资料 ,因此调用者必须decref,示例代码如下所示:

    PyObject *l, *x;
    
    l = PyList_New(3);
    x = PyInt_FromLong(1L);
    PySequence_SetItem(l, 0, x); Py_DECREF(x);
    x = PyInt_FromLong(2L);
    PySequence_SetItem(l, 1, x); Py_DECREF(x);
    x = PyString_FromString("three");
    PySequence_SetItem(l, 2, x); Py_DECREF(x);
    PyObject *l, *x;
    

    我的问题是,如果第二个示例类似于第一个从sometype传递pytype_的示例,会发生什么?

    PyObject *l;
    
    l = PyList_New(3);
    PySequence_SetItem(l, 0, PyInt_FromLong(1L));
    PySequence_SetItem(l, 1, PyInt_FromLong(2L));
    PySequence_SetItem(l, 2, PyString_FromString("three"));
    

    最后一种情况是良性的,还是会导致内存泄漏(因为pysequence_setitem不会拥有pyint_fromlong和pystring_fromstring创建的引用的所有权,调用方也不会对其进行解密)?是吗?

    1 回复  |  直到 7 年前
        1
  •  4
  •   abarnert    7 年前

    它会导致内存泄漏。

    创建对象时,它以refcount为1开始。只有当refcount变为0时,对象才会被删除。

    第一个例子 :将新对象传递给窃取引用(取得所有权)的函数时,如 PyTuple_SetItem ,refcount没有递增,所以仍然是1。当元组最终被销毁并减少其所有元素时,计数将降至0,因此它将被销毁。 一切都好。

    第三个例子 :将新对象传递给不窃取引用(生成新引用)的函数时,如 PySequence_SetItem ,引用计数 递增,所以是2。当元组最终被销毁并减少其所有元素时,计数将降至1,因此不会被销毁。而且,既然没有人再提到它了(除非你把它放在某个地方),就没有人能贬低它了。 所以它被泄露了。

    第二个例子 :将新对象传递给不窃取引用(生成新引用)的函数时,如 Pysequence设置项 ,然后打电话 Py_DECREF 在它上,refcount递增为2,递减为1。因此,当元组最终被销毁并减少其所有元素时,计数将降至0。 一切又好了。


    如果您想知道为什么python会同时使用任何非窃取函数,那么您只需要考虑一个不那么简单的情况。

    如果你想把东西放进去 元组而不是一个?或者如果你想把它放在一个元组中,但是也要把它存储在一个C静态指针中,或者某个模块的全局变量中,或者其他什么地方?如果要将引用计数存储在两个位置,则需要将其增加2,而当局部变量消失时,引用计数将减少1。对于一个非常简单的例子,您只需创建一些东西并立即将其传递出去,引用窃取函数可以让您避免一个incref和一个decref,并且对一行代码很友好和方便。但对于任何更复杂的事情,这都没有意义。