我正在进行C扩展,我正处于追踪内存泄漏的程度。从阅读Python的文档来看,很难理解何时增加/减少Python对象的引用计数。此外,经过几天花费尝试嵌入Python解释器(为了将扩展编译为独立程序),我不得不放弃这一努力。所以像Valgrind这样的工具在这里很无奈。
到目前为止,通过反复试验,我了解到,例如,Py_DECREF(Py_None)
是一件坏事......但这是否适用于任何常数?我不知道。
到目前为止我的主要困惑可以这样列出:
PyWhatever_New()
创建的任何内容都不会超过创建它的过程,我是否必须减少引用计数?Py_INCREF
是否需要与Py_DECREF
匹配,还是应该有一个/另一个?PyObject*
,我是否需要增加它以确保我仍然可以(永远)使用它,或者减少它以确保最终它将被垃圾收集,或者都不是?Py_INCREF
可能会在堆上重新分配它们。)答案 0 :(得分:3)
Reference Count Details中涵盖了大部分内容,其余内容将在您提出的具体问题的文档中介绍。但是,要把它全部集中在一个地方:
Py_DECREF(Py_None)
是一件坏事......但是任何常数都是如此吗?
更一般的规则是,在您没有获得新的/被盗的引用,并且没有调用Py_DECREF
的任何内容上调用Py_INCREF
是一件坏事。由于您永远不会在任何可以作为常量访问的内容上调用Py_INCREF
,这意味着您永远不会在其上调用Py_DECREF
。
我是否必须减少由
创建的任何内容的引用计数PyWhatever_New()
是。任何返回"新参考"必须减少。按照惯例,以_New
结尾的任何内容都应返回新的引用,但无论如何都应该记录(例如,请参阅PyList_New
)。
每个
Py_INCREF
是否需要与Py_DECREF
匹配,还是应该有一个/另一个?
您自己代码中的数字可能不一定平衡。 总号必须平衡,但Python本身内部会发生增量和减量。例如,任何返回"新引用的内容"已经做了一个公司,而任何"窃取"参考将对其进行决定。
通过堆栈或堆上分配的堆栈上的C API创建Python对象吗? (例如,Py_INCREF可能会在堆上重新分配它们。)
无法通过堆栈上的C API创建对象。 C API只有返回指向对象的指针的函数。
大多数这些对象都是在堆上分配的。有些实际上是在静态内存中。
但是你的代码无论如何都不应该关心。你永远不会分配或删除它们;它们会在PySpam_New
和类似函数中分配,并在Py_DECREF
将它们设置为0时自行释放,因此它们对您来说并不重要。
(除了你可以通过他们的全局名称访问的常量,例如Py_None
。那些,你显然知道它们在静态存储中。)
在将C代码创建的Python对象传递给Python代码之前,我是否需要做一些特殊的事情?
没有
如果Python代码比创建Python对象的C代码更长,该怎么办?
我不确定你的意思是什么?" outlives"这里。当任何对象依赖于其代码时,您的扩展模块将不会被卸载。 (事实上,直到至少3.8,你的模块可能永远不会在关闭之前卸载。)
如果您只是指_New
' d对象返回的功能,那不是问题。你必须走得太远,才能在堆栈上分配任何Python对象。并且没有办法将诸如C数组对象或C字符串之类的东西传递给Python代码而不将它们转换为Python元组对象,或Python字节或str。在某些情况下,例如,您可以在PyCapsule
中存储指向堆栈中某些内容的指针并传递该指针 - 但这与任何C程序中的相同,并且......只需要&#39这样做。
最后,据我所知,Python同时具有引用计数和垃圾收集器
垃圾收集器只是一个循环断路器。如果您的对象通过参考周期保持彼此活动,则可以依赖GC。但是,如果您泄漏了对象的引用,GC将永远不会清理它。