6

下面的函数接受一个 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);
}

谢谢参观,

拉森MTL

4

3 回答 3

17

PyDict_New()返回一个新的参考,检查文档中的PyDict. 因此,如果您在创建后立即增加引用计数,则您有两个对其的引用。当您将其作为结果值返回时,一个会转移给调用者,但另一个永远不会消失。

您也不需要 incref pyTimeList。当您创建它时,它就是您的。但是,您需要对其进行 decref,但您只需对其进行一次 decref,因此它也会泄漏。

您也不需要调用Py_INCREF. pyDB这是一个借用的引用,只要你的函数没有返回它就不会消失,因为它仍然在较低的堆栈帧中被引用。

只有当您想将引用保留在另一个结构中的某个地方时,您才需要增加引用计数。

参照。API文档

于 2008-12-08T20:04:32.643 回答
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);
}

请注意,您可能不会减少oafter call的引用计数PyList_SET_ITEM,因为它“窃取”了引用。检查文档

于 2008-12-08T20:12:44.017 回答
3

我不知道 Python-C。但是,我对 COM 引用计数的经验表明,新创建的引用计数对象的引用计数为1。所以你的 Py_INCREF(pyDB) 在 PyArg_ParseTuple(args, "O", &pyDB) 和 PyObject *pyDBHeader = PyDict_New(); 是罪魁祸首。他们的引用计数已经是 2。

于 2008-12-08T20:18:09.497 回答