下面的函数接受一个python文件句柄,从文件中读取打包的二进制数据,创建一个Python字典并返回它。如果我无休止地循环它,它将不断消耗RAM。我的RefCounting出了什么问题?
static PyObject* __binParse_getDBHeader(PyObject *self, PyObject *args){
PyObject *o; //generic object
PyObject* pyDB = NULL; //this has to be a py file object
if (!PyArg_ParseTuple(args, "O", &pyDB)){
return NULL;
} else {
Py_INCREF(pyDB);
if (!PyFile_Check(pyDB)){
Py_DECREF(pyDB);
PyErr_SetString(PyExc_IOError, "argument 1 must be open file handle");
return NULL;
}
}
FILE *fhDB = PyFile_AsFile(pyDB);
long offset = 0;
DB_HEADER *pdbHeader = malloc(sizeof(DB_HEADER));
fseek(fhDB,offset,SEEK_SET); //at the beginning
fread(pdbHeader, 1, sizeof(DB_HEADER), fhDB );
if (ferror(fhDB)){
fclose(fhDB);
Py_DECREF(pyDB);
PyErr_SetString(PyExc_IOError, "failed reading database header");
return NULL;
}
Py_DECREF(pyDB);
PyObject *pyDBHeader = PyDict_New();
Py_INCREF(pyDBHeader);
o=PyInt_FromLong(pdbHeader->version_number);
PyDict_SetItemString(pyDBHeader, "version", o);
Py_DECREF(o);
PyObject *pyTimeList = PyList_New(0);
Py_INCREF(pyTimeList);
int i;
for (i=0; i<NUM_DRAWERS; i++){
//epochs
o=PyInt_FromLong(pdbHeader->last_good_test[i]);
PyList_Append(pyTimeList, o);
Py_DECREF(o);
}
PyDict_SetItemString(pyDBHeader, "lastTest", pyTimeList);
Py_DECREF(pyTimeList);
o=PyInt_FromLong(pdbHeader->temp);
PyDict_SetItemString(pyDBHeader, "temp", o);
Py_DECREF(o);
free(pdbHeader);
return (pyDBHeader);
}
谢谢你看看,
LarsenMTL
答案 0 :(得分:16)
PyDict_New()
会返回新的引用,请检查PyDict
pyTimeList
。因此,如果您在创建后立即增加引用计数,则会有两个引用它。当你把它作为结果值返回时,一个被转移到调用者,但是另一个永远不会消失。
您也不需要递增Py_INCREF
。它是你的,当你创建它。但是,你需要减少它,但你只减少它一次,所以它也被泄露了。
您也无需在pyDB
上致电{{1}}。它是一个借用的引用,只要你的函数没有返回它就不会消失,因为它仍然在较低的堆栈帧中引用。
只有当您想要将引用保留在某个地方的其他结构中时,才需要增加引用计数。
比照。 docs
答案 1 :(得分:5)
OT:使用PyList_Append
的连续调用是一个性能问题。由于您知道您将提前获得多少结果,因此您可以使用:
PyObject *pyTimeList = PyList_New(NUM_DRAWERS);
int i;
for (i=0; i<NUM_DRAWERS; i++){
o = PyInt_FromLong(pdbHeader->last_good_test[i]);
PyList_SET_ITEM(pyTimeList, i, o);
}
请注意,在调用o
后,您可能不会减少PyList_SET_ITEM
的引用次数,因为它会“窃取”引用。查看docs。
答案 2 :(得分:2)
我不知道Python-C。但是,我对COM引用计数的经验表明,新创建的引用计数对象的引用计数 1 。因此PyArg_ParseTuple(args,“O”,&amp; pyDB)和PyObject * pyDBHeader = PyDict_New()之后的Py_INCREF(pyDB);是罪魁祸首。他们的参考计数已经是2。