我正在使用Python C API为Python编写C ++包装器。在我的情况下,我必须为Python脚本提供更大量的面向字节的数据。为此,我使用PyByteArray_FromStringAndSize
方法生成Python bytearray(https://docs.python.org/2.7/c-api/bytearray.html)。
直接返回此bytearray时,我没有遇到任何问题。然而,当将bytearray添加到Python dict中时,一旦dict被破坏,bytearray中的内存将不会被释放。
这可以通过在将bytearray对象添加到Python dict之后调用bytearray对象上的Py_DECREF
来解决。
下面是我的代码的完整工作示例,其中包含返回普通bytearray的方法dummyArrPlain
以及在dict中返回bytearray的方法dummyArrInDict
。除非调用Py_DECREF(pyData);
,否则第二种方法将产生内存泄漏。
我的问题是:为什么Py_DECREF
此时需要。直觉上,我预计一旦dict被销毁就应该调用Py_DECREF
。
此外,我将如下所示的值分配给dict:
PyDict_SetItem(dict, PyString_FromString("i"), PyInt_FromLong(i));
如果没有在创建的字符串上调用Py_DECREF
并且长?
这是我的虚拟C ++包装器:
#include <python2.7/Python.h>
static char module_docstring[] = "This is a module causing a memory leak";
static PyObject *dummyArrPlain(PyObject *self, PyObject *args);
static PyObject *dummyArrInDict(PyObject *self, PyObject *args);
static PyMethodDef module_methods[] = {
{"dummy_arr_plain", dummyArrPlain, METH_VARARGS, "returns a plain dummy bytearray"},
{"dummy_arr_in_dict", dummyArrInDict, METH_VARARGS, "returns a dummy bytearray in a dict"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initlibdummy(void)
{
PyObject *m = Py_InitModule("libdummy", module_methods);
if (m == NULL)
return;
}
static PyObject *dummyArrPlain(PyObject *self, PyObject *args)
{
int len = 10000000;
char* data = new char[len];
for(int i=0; i<len; i++) {
data[i] = 0;
}
PyObject * pyData = PyByteArray_FromStringAndSize(data, len);
delete [] data;
return pyData;
}
static PyObject *dummyArrInDict(PyObject *self, PyObject *args)
{
int len = 10000000;
char* data = new char[len];
for(int i=0; i<len; i++) {
data[i] = 0;
}
PyObject * pyData = PyByteArray_FromStringAndSize(data, len);
delete [] data;
PyObject *dict = PyDict_New();
PyDict_SetItem(dict, PyString_FromString("data"), pyData);
// memory leak without Py_DECREF(pyData);
return dict;
}
使用包装器的虚拟python脚本:
import libdummy
import time
while True:
a = libdummy.dummy_arr_in_dict()
time.sleep(0.01)
答案 0 :(得分:1)
这是[Python 2.0.Docs]: Ownership rules的问题。我将在 Python 2.7.10 上举例说明(相当古老,但我并不认为这种行为已经(显着)改变了。)
PyByteArray_FromStringAndSize ( bytearrayobject.c : 168 )创建一个新对象(使用 PyObject_New ,并分配内存对于缓冲区也是如此。
默认情况下,该对象的引用计数(或更好:任何新创建的对象) 1 (由 _Py_NewReference 设置),因此,当用户在其上或程序退出时调用 del 时,引用计数将减少,当达到0时,将取消分配该对象。
这是返回对象的流程上的行为
但是,在 dummyArrInDict 的情况下, PyDict_SetItem (间接地) pyData的 Py_INCREF (它做其他事情,但只有这在当前情况下是相关的),最后是 2 的引用因此内存泄漏
与您使用数据进行的操作基本相同:您为它分配内存,当您不再需要它时,您可以释放它(这是因为您和#39;不归还它,你只能暂时使用它。
注意:使用 X 宏(例如[Python 2.Docs]: Py_XDECREF)更安全,特别是因为您没有测试 NULL 返回的 PyObject s)。
有关详细信息,请查看[Python 2.Docs]: C API Reference。