如何 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;
...
}
在这种情况下,
tp_next()
return 是对 linecache[0]
的拥有引用,因为它正在递增其 Py_XINCREF
?
- 意思是,我的
linecache[0]
缓存现在是借用的引用吗?
- 由于
tp_next()
被调用不止一次,而且我 return 对同一指针多次递增其 Py_XINCREF
,这是否会导致 double/triple/several 释放?
- 是否
tp_next()
return 对象只有一个拥有引用,这将导致 double/triple/several?
相关:
- Py_INCREF/DECREF: When
你基本上需要一个对象的引用计数等于引用它的PyObject*
个数。
是的 - 你 return 一个 PyObject*
到你的 Python 代码,所以你应该增加引用计数。
否 - 创建 linecache[0]
时,它的引用计数为 1,这代表 linecache
的所有权。多个地方可以 "own" 一个 Python 对象。
是的,您 return 多次使用同一个指针;不,这不会导致多次释放。当引用计数达到 0 时,指针将被释放。这将是当您破坏对从 next
中 return 编辑的值的所有引用时,以及当您丢失 linecache
中的引用时](当调用 PyFastFile_dealloc
时)。
最后一个问题没看懂,不过这里的代码基本正确
我在这里看到的一期是 "what is linecache
/who owns it"。如果它是一个全局变量,那么它最终可能会在多个 PyFastFile
对象之间共享,这可能是错误的。销毁单个 PyFastFile
将导致整个 linecache
被释放,但您不会 pop_back
或 NULL
指针。
在我的最后一个问题
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;
...
}
在这种情况下,
tp_next()
return 是对linecache[0]
的拥有引用,因为它正在递增其Py_XINCREF
?- 意思是,我的
linecache[0]
缓存现在是借用的引用吗? - 由于
tp_next()
被调用不止一次,而且我 return 对同一指针多次递增其Py_XINCREF
,这是否会导致 double/triple/several 释放? - 是否
tp_next()
return 对象只有一个拥有引用,这将导致 double/triple/several?
相关:
- Py_INCREF/DECREF: When
你基本上需要一个对象的引用计数等于引用它的PyObject*
个数。
是的 - 你 return 一个
PyObject*
到你的 Python 代码,所以你应该增加引用计数。否 - 创建
linecache[0]
时,它的引用计数为 1,这代表linecache
的所有权。多个地方可以 "own" 一个 Python 对象。是的,您 return 多次使用同一个指针;不,这不会导致多次释放。当引用计数达到 0 时,指针将被释放。这将是当您破坏对从
next
中 return 编辑的值的所有引用时,以及当您丢失linecache
中的引用时](当调用PyFastFile_dealloc
时)。最后一个问题没看懂,不过这里的代码基本正确
我在这里看到的一期是 "what is linecache
/who owns it"。如果它是一个全局变量,那么它最终可能会在多个 PyFastFile
对象之间共享,这可能是错误的。销毁单个 PyFastFile
将导致整个 linecache
被释放,但您不会 pop_back
或 NULL
指针。