我有一个巨大的内存泄漏问题,涉及到我正在开发的一个C扩展。在C中,我有一个名为A
的double数组和一个名为AnotherIntVariable
的int变量,我想传递给Python。嗯,在我的C扩展模块中,我做了以下事情:
int i;
PyObject *lst = PyList_New(len_A);
PyObject *num;
if(!lst)
return NULL;
for(i=0;i<len_A;i++){
num=PyFloat_FromDouble(A[i]);
if(!num){
Py_DECREF(lst);
return NuLL;
}
PyList_SET_ITEM(lst,i,num);
}
free(A);
return Py_BuildValue("Oi",lst,AnotherIntVariable)
字符串
所以在Python中,我接收这个列表和int如下:
Pyt_A,Pyt_int=MyCModule.MyCFunction(...)
型
其中Pyt_A和Pyt_int是我从C扩展“MyCModule
“中获得的列表和整数,来自我之前描述的函数“MyCFunction
“。
问题是,在Python中,我使用这个Pyt_A
数组(这就是为什么我使用Py_BuildValue
而不是一个简单的return
语句来执行INCREF,以便从垃圾收集器中保存这个变量一会儿),但是我需要以某种方式解引用它,以便释放分配的内存。问题是我多次使用MyCFunction
函数,这会产生内存泄漏,因为我不知道如何取消引用Python中的数组,以摆脱它。
我试着在C部分的代码中只返回一个return lst
而不是Py_BuildValue("Oi",lst,AnotherIntVariable)
,但是当我试图在python中使用它时,只会导致一个Segmentation Fault(可能是因为垃圾收集器做了他的工作)。
……我错过了什么?有人能帮我吗?
3条答案
按热度按时间2vuwiymt1#
如果查看
Py_BuildValue
的文档(http://docs.python.org/3/c-api/arg.html#c.Py_BuildValue),您可以看到在O
类型代码下,它说传入对象的引用计数递增1(注意:该页面前面的部分描述了PyArg_ParseTuple
的O
类型代码,它 * 不 * 递增引用计数,但在这里也不相关)。因此,在调用
Py_BuildValue
之后,列表的引用计数是2
,但您只希望它是1
。不直接返回
Py_BuildValue
的结果,而是将其保存到PyObject
指针,递减lst
引用计数,然后返回结果。无论如何,您都应该检查
Py_BuildValue
调用的结果,因为您还需要在Py_BuildValue
失败时释放num
(即返回NULL
)。zz2j4svz2#
谢谢你清理了伊格纳西奥,现在它变得很有意义了!最后,解决方案是,而不是直接返回Py_BuildValue,这样做:
字符串
它像一个魅力!
4xy9mtcn3#
有一个更简单的解决方案是其他答案所建议的。在使用
O
代码(它对传递的对象进行新的强引用)后,不要重新排列代码以减少引用,只需使用N
代码,它会 * 窃取 * 您的引用,完全通过转移所有权来避免不必要的引用计数操作。对此需要的 * 唯一 * 更改是更改:字符串
收件人:
型
Py_BuildValue
也非常健壮;如果由于任何原因失败,它将继续解析参数,以确保引用计数被窃取并正确释放。(lst
)作为NULL
传递(通常是因为在参数列表中调用了返回新强引用的函数并失败,设置一个异常),它保留该异常并返回NULL
本身,因此该异常按预期传播。