我已经将程序中的内存泄漏追溯到我用 C 编写的 Python 模块,以有效地解析以 ASCII-hex 表示的数组。(例如"FF 39 00 FC ...")
char* buf;
unsigned short bytesPerTable;
if (!PyArg_ParseTuple(args, "sH", &buf, &bytesPerTable))
{
return NULL;
}
unsigned short rowSize = bytesPerTable;
char* CArray = malloc(rowSize * sizeof(char));
// Populate CArray with data parsed from buf
ascii_buf_to_table(buf, bytesPerTable, rowSize, CArray);
int dims[1] = {rowSize};
PyObject* pythonArray = PyArray_SimpleNewFromData(1, (npy_intp*)dims, NPY_INT8, (void*)CArray);
return Py_BuildValue("(O)", pythonArray);
我意识到numpy不知道释放分配给CArray的内存,从而导致内存泄漏。在对这个问题进行了一些研究之后,在本文的评论建议下,我添加了以下行,该行应该告诉数组它"拥有"其数据,并在删除时释放它。
PyArray_ENABLEFLAGS((PyArrayObject*)pythonArray, NPY_ARRAY_OWNDATA);
但我仍然得到内存泄漏。我做错了什么?如何使NPY_ARRAY_OWNDATA标志正常工作?
作为参考,ndarraytypes.h 中的文档看起来应该可以工作:
/*
* If set, the array owns the data: it will be free'd when the array
* is deleted.
*
* This flag may be tested for in PyArray_FLAGS(arr).
*/
#define NPY_ARRAY_OWNDATA 0x0004
同样作为参考,以下代码(调用 C 中定义的 Python 函数)演示了内存泄漏。
tableData = "FF 39 00 FC FD 37 FF FF F9 38 FE FF F1 39 FE FC n"
"EF 38 FF FE 47 40 00 FB 3D 3B 00 FE 41 3D 00 FE n"
"43 3E 00 FF 42 3C FE 02 3C 40 FD 02 31 40 FE FF n"
"2E 3E FF FE 24 3D FF FE 15 3E 00 FC 0D 3C 01 FA n"
"02 3E 01 FE 01 3E 00 FF F7 3F FF FB F4 3F FF FB n"
"F1 3D FE 00 F4 3D FE 00 F9 3E FE FC FE 3E FD FE n"
"F6 3E FE 02 03 3E 00 FE 04 3E 00 FC 0B 3D 00 FD n"
"09 3A 00 01 03 3D 00 FD FB 3B FE FB FD 3E FD FF n"
for i in xrange(1000000):
PES = ParseTable(tableData, 128, 4) //Causes memory usage to skyrocket
这可能是一个引用计数问题(来自如何扩展 NumPy):
引用计数错误的一个常见来源是Py_BuildValue函数。请特别注意"N"格式字符和"O"格式字符之间的区别。如果您在子例程(例如输出数组)中创建一个新对象,并且您要在返回值的元组中将其传递回,那么您最有可能在Py_BuildValue中使用"N"格式字符。"O"字符会将引用计数增加 1。这将给调用方留下一个全新数组的两个引用计数。当删除变量并且引用计数减少 1 时,仍将存在该额外的引用计数,并且数组将永远不会被解除分配。您将遇到引用计数引起的内存泄漏。使用"N"字符将避免这种情况,因为它将向调用方返回具有单个引用计数的对象(元组内)。