下面的函数接受一个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; ilast_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
PyDict_New()
返回一个新的参考,请查看文档的PyDict
.因此,如果您在创建后立即增加引用计数,则会有两个引用它.当你将它作为结果值返回时,一个被转移到调用者,但另一个永远不会消失.
你也不需要增加pyTimeList
.它是你的,当你创建它.但是,你需要减少它,但你只减少它一次,所以它也被泄露了.
你也不需要调用Py_INCREF
上pyDB
.它是一个借用的引用,只要你的函数没有返回它就不会消失,因为它仍然在较低的堆栈帧中引用.
只有当您想将引用保留在某个地方的另一个结构中时,才需要增加引用计数.
参看 该API文档
OT:使用连续调用PyList_Append
是性能问题.既然您知道您将提前获得多少结果,您可以使用:
PyObject *pyTimeList = PyList_New(NUM_DRAWERS); int i; for (i=0; ilast_good_test[i]); PyList_SET_ITEM(pyTimeList, i, o); }
请注意,您可能不会减少o
调用后的引用计数PyList_SET_ITEM
,因为它"窃取"了引用.检查文档.