如何 return 多次缓存 PyObject* 值而不发生内存泄漏或 double/triple 释放?

How to return a cached PyObject* value several times without memory leaking or double/triple frees?

在我的最后一个问题 中提出了我的一些记忆问题。然后,我写了这个关于 Python C Extension 的简单而可笑的用法,只是为了试图更好地理解 Python 借用和拥有参考文献。

static void PyFastFile_dealloc(PyFastFile* self) {
    for( PyObject* pyobject : linecache ) {
        Py_XDECREF( pyobject );
    }
    Py_TYPE(self)->tp_free( (PyObject*) self );
}

static PyObject* PyFastFile_tp_iter(PyFastFile* self, PyObject* args) {
    counter = 10;
    std::string line{"sample line"}
    PyObject* obj = PyUnicode_DecodeUTF8( line.c_str(), line.size(), "replace" );
    linecache.push_back( obj );
}

static PyObject* PyFastFile_iternext(PyFastFile* self, PyObject* args) {
    --counter;

    if( !( counter ) ) {
        PyErr_SetNone( PyExc_StopIteration );
        return NULL;
    }

    PyObject* retval = linecache[0];
    Py_XINCREF( retval );
    return retval;
}

// create the module
PyMODINIT_FUNC PyInit_fastfilepackage(void) {
    PyFastFileType.tp_iter = (getiterfunc) PyFastFile_tp_iter;
    PyFastFileType.tp_iternext = (iternextfunc) PyFastFile_iternext;
    PyFastFileType.tp_dealloc = (destructor) PyFastFile_dealloc;
    ...
}

在这种情况下,

  1. tp_next() return 是对 linecache[0] 的拥有引用,因为它正在递增其 Py_XINCREF
  2. 意思是,我的 linecache[0] 缓存现在是借用的引用吗?
  3. 由于 tp_next() 被调用不止一次,而且我 return 对同一指针多次递增其 Py_XINCREF,这是否会导致 double/triple/several 释放?
  4. 是否 tp_next() return 对象只有一个拥有引用,这将导致 double/triple/several?

相关:

  1. Py_INCREF/DECREF: When

你基本上需要一个对象的引用计数等于引用它的PyObject*个数。

  1. 是的 - 你 return 一个 PyObject* 到你的 Python 代码,所以你应该增加引用计数。

  2. 否 - 创建 linecache[0] 时,它的引用计数为 1,这代表 linecache 的所有权。多个地方可以 "own" 一个 Python 对象。

  3. 是的,您 return 多次使用同一个指针;不,这不会导致多次释放。当引用计数达到 0 时,指针将被释放。这将是当您破坏对从 next 中 return 编辑的值的所有引用时,以及当您丢失 linecache 中的引用时](当调用 PyFastFile_dealloc 时)。

  4. 最后一个问题没看懂,不过这里的代码基本正确


我在这里看到的一期是 "what is linecache/who owns it"。如果它是一个全局变量,那么它最终可能会在多个 PyFastFile 对象之间共享,这可能是错误的。销毁单个 PyFastFile 将导致整个 linecache 被释放,但您不会 pop_backNULL 指针。