Python 中的 C 扩展 - 返回 Py_BuildValue() 内存泄漏问题
我有一个巨大的内存泄漏问题,涉及我正在开发的 C 扩展。在 C 中,我有一个名为 A
的双精度数组和一个名为 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”获取的列表和整数code>”,来自我之前描述的函数“
MyCFunction
”。
问题是,在 Python 中,我使用这个 Pyt_A
数组(这就是为什么我使用 Py_BuildValue
而不是简单的 return
语句来执行INCREF 以便从垃圾收集器中暂时保存该变量),但随后我需要以某种方式取消引用它,以便释放分配的内存。问题是我多次使用 MyCFunction 函数,这会产生内存泄漏,因为我不知道如何取消引用在 python 中获得的数组以摆脱它。
我尝试通过在代码的 C 部分执行 return lst
而不是 Py_BuildValue("Oi",lst,AnotherIntVariable)
来返回数组,但这只会产生结果当我尝试在 python 中使用它时出现分段错误(可能是因为垃圾收集器完成了他的工作)...
...我在这里缺少什么?有人可以帮助我吗?
I have a huge memory leak problem involving a C-extension I'm developing. In C, I have an array of doubles called A
and an int variable called AnotherIntVariable
that I want to pass to Python. Well, in my C-extension module I do the following:
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)
So in Python i recieve this list and the int like this:
Pyt_A,Pyt_int=MyCModule.MyCFunction(...)
Where Pyt_A and Pyt_int are the list and the integer I get from my C-extension "MyCModule
", from the function "MyCFunction
" that I described earlier.
The problem is that, in Python, I use this Pyt_A
array (so that's why I use Py_BuildValue
instead of a simple return
statement, to do an INCREF in order to save this variable for a moment from the garbage collector) but then I need to dereference it somehow in order to free that allocated memory. The problem is that I use the MyCFunction
function several times, and this produces a memory leakage because I don't know how to dereference the array that I get in python in order to get rid of it.
I tried just returning the array by doing a return lst
in the C part of the code instead of the Py_BuildValue("Oi",lst,AnotherIntVariable)
, but that only results in a Segmentation Fault when I try to use it in python (probably because the garbage collector did his work)...
...what am I missing here? Can anybody help me?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果您查看
Py_BuildValue
的文档 (http://docs.python.org/3/c-api/arg.html#c.Py_BuildValue)您可以看到,在O
类型代码下,它表示传入对象的引用计数增加一(注意:该页面的前面部分描述了PyArg_ParseTuple
的O
类型代码,但没有 em> 增加引用计数,但这里也不相关)。因此,在调用
Py_BuildValue
后,列表的引用计数为2
,但您只希望它为1
。不要直接返回
Py_BuildValue
的结果,而是将其保存到PyObject
指针,递减lst
引用计数,然后返回结果。无论如何,您应该检查
Py_BuildValue
调用的结果,因为您还需要在Py_BuildValue
失败时释放num
(即返回 <代码>NULL)。If you look at the documentation for
Py_BuildValue
(http://docs.python.org/3/c-api/arg.html#c.Py_BuildValue) you can see that under theO
typecode, it says that the reference count of the passed in object is incremented by one (Note: an earlier section in that page describes theO
typecode forPyArg_ParseTuple
, which doesn't increment the reference count, but also isn't relevant here).So, after the call to
Py_BuildValue
, the refcount for your list is2
, but you only want it to be1
.Instead of returning the result of
Py_BuildValue
directly, save it to aPyObject
pointer, decrement thelst
reference count, then return your result.You should be checking the result of the
Py_BuildValue
call anyway, since you also need to freenum
in the event thatPy_BuildValue
fails (i.e. returnsNULL
).感谢伊格纳西奥的澄清,现在它变得非常有意义了!最后,解决方案是,不要直接返回 Py_BuildValue,而是这样做:
它就像一个魅力!
Thanks for clearing it up Ignacio, now it makes so much sense! Finally, the solution was to, instead of returning directly the Py_BuildValue, do:
It worked like a charm!
有一个比其他答案所建议的更简单的解决方案。使用
O
格式代码(对传递的对象采用新的强引用)后,不要将代码重新排列为 decref,只需使用N
格式代码,该代码 < em>窃取您的引用,通过转移所有权完全避免不必要的引用计数操作。为此所需的唯一更改是更改为:
Py_BuildValue
也非常强大;如果由于任何原因失败,它会继续解析参数以确保引用计数被窃取并适当释放。如果N
参数 (lst
) 作为NULL
传递(通常是因为在参数列表中调用了返回新强引用的函数并且失败,设置异常)它保留该异常并返回 NULL 本身,以便异常按预期传播。There's a simpler solution that what the other answers have been suggesting. Don't rearrange your code to decref after using the
O
format code (which takes a new strong reference to the passed object), just use theN
format code, which steals your reference, avoiding unnecessary reference count manipulation entirely by transferring ownership. The only change needed for this is to change:to:
Py_BuildValue
is pretty robust too; if it fails for any reason it continues parsing arguments to ensure reference counts are stolen and released appropriately. If theN
argument (lst
) is passed asNULL
(usually because a function returning a new strong reference was called in the argument list and failed, setting an exception) it preserves that exception and returnsNULL
itself so the exception propagates as intended.