Python中的C扩展- return Py_BuildValue()内存泄漏问题

sdnqo3pr  于 12个月前  发布在  Python
关注(0)|答案(3)|浏览(141)

我有一个巨大的内存泄漏问题,涉及到我正在开发的一个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(可能是因为垃圾收集器做了他的工作)。
……我错过了什么?有人能帮我吗?

2vuwiymt

2vuwiymt1#

如果查看Py_BuildValue的文档(http://docs.python.org/3/c-api/arg.html#c.Py_BuildValue),您可以看到在O类型代码下,它说传入对象的引用计数递增1(注意:该页面前面的部分描述了PyArg_ParseTupleO类型代码,它 * 不 * 递增引用计数,但在这里也不相关)。
因此,在调用Py_BuildValue之后,列表的引用计数是2,但您只希望它是1
不直接返回Py_BuildValue的结果,而是将其保存到PyObject指针,递减lst引用计数,然后返回结果。
无论如何,您都应该检查Py_BuildValue调用的结果,因为您还需要在Py_BuildValue失败时释放num(即返回NULL)。

zz2j4svz

zz2j4svz2#

谢谢你清理了伊格纳西奥,现在它变得很有意义了!最后,解决方案是,而不是直接返回Py_BuildValue,这样做:

free(A);  
PyObject *MyResult = Py_BuildValue("Oi",lst,AnotherIntVariable);  
Py_DECREF(lst);  
return MyResult

字符串
它像一个魅力!

4xy9mtcn

4xy9mtcn3#

有一个更简单的解决方案是其他答案所建议的。在使用O代码(它对传递的对象进行新的强引用)后,不要重新排列代码以减少引用,只需使用N代码,它会 * 窃取 * 您的引用,完全通过转移所有权来避免不必要的引用计数操作。对此需要的 * 唯一 * 更改是更改:

return Py_BuildValue("Oi",lst,AnotherIntVariable);

字符串
收件人:

return Py_BuildValue("Ni",lst,AnotherIntVariable);
//                    ^ only change


Py_BuildValue也非常健壮;如果由于任何原因失败,它将继续解析参数,以确保引用计数被窃取并正确释放。(lst)作为NULL传递(通常是因为在参数列表中调用了返回新强引用的函数并失败,设置一个异常),它保留该异常并返回NULL本身,因此该异常按预期传播。

相关问题