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

9

我正在开发一款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中,我会像这样接收列表和整数:

Pyt_A,Pyt_int=MyCModule.MyCFunction(...)

在我的C扩展模块"MyCModule"中,我从函数"MyCFunction"中获取到的列表和整数是Pyt_APyt_int。问题是,在Python中,我使用这个Pyt_A数组(所以我使用Py_BuildValue而不是一个简单的return语句,进行INCREF以便暂时保存这个变量,避免它被垃圾收集器清除),但是我需要以某种方式取消引用,以便释放已分配的内存。问题是,我多次使用MyCFunction函数,这导致了内存泄漏,因为我不知道如何取消Python中获得的数组的引用,以摆脱它。

我尝试通过在代码的C部分执行return lst,而不是Py_BuildValue("Oi",lst,AnotherIntVariable)来返回数组,但是当我尝试在Python中使用它时,只会出现Segmentation Fault(可能是因为垃圾收集器起作用了)...

......我错过了什么?有人可以帮我吗?


在Python中,使用Pyt_A的DECREF函数会减少数组的引用计数并释放其内存空间,但不会删除该数组。 - highBandWidth
我尝试过了,但似乎不起作用。我仍然遇到这个内存泄漏问题,就像我所说的那样。 - Néstor
1
另外,你确保在 Py_BuildValue("Oi",lst,AnotherIntVariable) 之前有一个 return 语句了吗? - highBandWidth
是的,我在帖子的编辑中错过了它。 - Néstor
在Python中,sys.getrefcount(Pyt_A)是什么意思? - highBandWidth
sys.getrefcount(Pyt_A) 的值为 3。 - Néstor
2个回答

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

文档现在说“对象的引用计数不会增加” - 那到底是哪个? - Eric
1
Py_BuildValue 的目标链接锚点已更改,因此链接指向页面顶部,覆盖了 PyArg_ParseTuple 格式代码。我修复了链接,并添加了有关可能会引起混淆的条目的注释。 - ncoghlan

3
感谢您解释清楚,现在我理解了!最终的解决方案是,不直接返回Py_BuildValue,而是做如下更改:
free(A);  
PyObject *MyResult = Py_BuildValue("Oi",lst,AnotherIntVariable);  
Py_DECREF(lst);  
return MyResult

它像魔法一样奏效了!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接