首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么我的Python C扩展会泄漏内存?

为什么我的Python C扩展会泄漏内存?
EN

Stack Overflow用户
提问于 2008-12-08 19:51:04
回答 3查看 4.9K关注 0票数 6

下面的函数接受一个python文件句柄,从文件中读取压缩的二进制数据,创建一个Python字典并返回它。如果我无休止地循环它,它将不断地消耗RAM。我的RefCounting怎么了?

代码语言:javascript
复制
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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2008-12-08 20:04:33

PyDict_New()返回一个新的引用,请检查docs中的PyDict。因此,如果在创建它之后立即增加refcount,就会有两个对它的引用。当您将其作为结果值返回时,一个函数会被传递给调用者,而另一个函数则永远不会消失。

你也不需要增加pyTimeList。当你创建它时,它就是你的了。但是,您需要对其进行减量,但您只对其减量一次,因此它也会泄漏。

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

只有当你想将引用保存在另一个结构中时,你才需要增加refcount的数量。

请参阅the API docs

票数 17
EN

Stack Overflow用户

发布于 2008-12-08 20:12:44

OT:使用对PyList_Append的连续调用是一个性能问题。因为你知道你会提前得到多少结果,所以你可以使用:

代码语言:javascript
复制
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);
}

注意,在调用PyList_SET_ITEM之后,您不能减少o的引用计数,因为它“窃取”了一个引用。检查docs

票数 5
EN

Stack Overflow用户

发布于 2008-12-08 20:18:09

我不知道Python-C。但是,根据我使用COM引用计数的经验,新创建的引用计数对象的引用计数为1。因此,PyArg_ParseTuple(args,"O",&pyDB)之后的Py_INCREF(pyDB)和PyObject *pyDBHeader = PyDict_New();是罪魁祸首。它们的引用计数已经是2。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/350647

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档