Python C 包装器内存泄漏
Posted
技术标签:
【中文标题】Python C 包装器内存泄漏【英文标题】:Python C Wrapper Memory Leak 【发布时间】:2015-02-17 20:47:57 【问题描述】:我在 python 和 C 方面有一定的经验,但对于将 python 模块编写为 C 函数的包装器是新手。对于一个项目,我需要一个名为“score”的函数,它的运行速度比我在 python 中的运行速度要快得多,所以我用 C 编写了它,实际上只是希望能够从 python 调用它。它接受一个整数的python列表,我希望C函数获取一个整数数组,该数组的长度,然后将一个整数返回给python。这是我当前(工作)的解决方案。
static PyObject *module_score(PyObject *self, PyObject *args)
int i, size, value, *gene;
PyObject *seq, *data;
/* Parse the input tuple */
if (!PyArg_ParseTuple(args, "O", &data))
return NULL;
seq = PySequence_Fast(data, "expected a sequence");
size = PySequence_Size(seq);
gene = (int*) PyMem_Malloc(size * sizeof(int));
for (i = 0; i < size; i++)
gene[i] = PyInt_AsLong(PySequence_Fast_GET_ITEM(seq, i));
/* Call the external C function*/
value = score(gene, size);
PyMem_Free(gene);
/* Build the output tuple */
PyObject *ret = Py_BuildValue("i", value);
return ret;
这可行,但似乎以我无法忽视的速度泄漏内存。我通过暂时使 score 函数返回 0 并仍然看到泄漏行为来确保在显示的函数中发生泄漏。我曾认为对 PyMem_Free 的调用应该处理 PyMem_Malloc 的存储,但我目前的猜测是,这个函数中的某些东西在每次调用时都会被分配和保留,因为泄漏行为与调用这个函数的次数成正比。我是否没有正确执行序列到数组的转换,或者我是否可能低效地返回结束值?任何帮助表示赞赏。
【问题讨论】:
它认为,Python 有一个内存池,调用PyMem_Free
不会立即释放指针。它将在内部释放它,以便 Python 可以重用它而无需再次分配它。但是,我不确定。
你在 Linux 上吗?你是如何确定有泄漏的?
我在 Windows 上使用 cygwin,这是一个类似 Linux 的环境。我使用 windows 任务管理器和 top 来查看 python 进程消耗越来越多的内存,然后随机等待打印,以查看内存仅在调用此函数而不是其他 python 函数时才增长。
我会推荐一个memory debugging tool
,对于Linux,有一个很棒的叫valgrind
,你可以在网上搜索任何类似的Windows 工具。您发布的功能,至少在我看来,一点也不错。
请注意,您确实有一个问题,即您将内存分配为sizeof(int)*size
,但您使用longs
而不是int
s 填充它。
【参考方案1】:
seq
是一个新的 Python 对象,因此您需要删除该对象。你也应该检查seq
是否为NULL。
类似的东西(未经测试):
static PyObject *module_score(PyObject *self, PyObject *args)
int i, size, value, *gene;
long temp;
PyObject *seq, *data;
/* Parse the input tuple */
if (!PyArg_ParseTuple(args, "O", &data))
return NULL;
if (!(seq = PySequence_Fast(data, "expected a sequence")))
return NULL;
size = PySequence_Size(seq);
gene = (int*) PyMem_Malloc(size * sizeof(int));
for (i = 0; i < size; i++)
temp = PyInt_AsLong(PySequence_Fast_GET_ITEM(seq, i));
if (temp == -1 && PyErr_Occurred())
Py_DECREF(seq);
PyErr_SetString(PyExc_ValueError, "an integer value is required");
return NULL;
/* Do whatever you need to verify temp will fit in an int */
gene[i] = (int*)temp;
/* Call the external C function*/
value = score(gene, size);
PyMem_Free(gene);
Py_DECREF(seq):
/* Build the output tuple */
PyObject *ret = Py_BuildValue("i", value);
return ret;
【讨论】:
对不起,我是新手...如何删除 seq 对象?我在您的示例中没有看到您删除它的任何地方。它不应该被释放,除非它是用 calloc 或 malloc 创建的,对吧? 我更新了答案。要删除 Python 对象,请使用Py_DECREF()
。我还检查了PyInt_AsLong()
的返回值,并在转换为 int 之前添加了一个验证值大小的位置。
谢谢,就是这样!现在不再有内存泄漏。以上是关于Python C 包装器内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章