什么时候使用 Py_INCREF?

wvx*_*xvw 5 python memory-management cpython

我正在开发一个 C 扩展,现在我想追踪内存泄漏。通过阅读 Python 的文档,很难理解何时增加/减少 Python 对象的引用计数。此外,在花了几天时间尝试嵌入 Python 解释器(为了将扩展编译为独立程序)之后,我不得不放弃这项努力。所以,像 Valgrind 这样的工具在这里是无能为力的。

到目前为止,通过反复试验,我了解到,例如,这Py_DECREF(Py_None)是一件坏事……但这是否适用于任何常数?我不知道。

到目前为止,我的主要困惑可以这样列出:

  1. PyWhatever_New()如果它不超过创建它的程序,我是否必须减少它创建的任何东西的refcount?
  2. 每个都Py_INCREF需要与 匹配Py_DECREF,还是应该有一个/另一个?
  3. 如果对 Python 过程的调用导致 a PyObject*,我是否需要增加它以确保我仍然可以使用它(永远),或者减少它以确保最终它会被垃圾收集,或者两者都不需要?
  4. Python 对象是通过 C API 在堆栈上还是堆上分配的堆栈上创建的?(例如,有可能Py_INCREF在堆上重新分配它们)。
  5. 在将它们传递给 Python 代码之前,我需要对在 C 代码中创建的 Python 对象做任何特殊的事情吗?如果 Python 代码比创建 Python 对象的 C 代码寿命长会怎样?
  6. 最后,我明白 Python 既有引用计数又有垃圾收集器:如果是这样的话,如果我把引用计数弄乱了(即递减得不够),GC 最终会弄清楚如何处理这些对象吗?

aba*_*ert 4

引用计数详细信息中涵盖了大部分内容中介绍,其余内容在有关您所询问的具体问题的文档中介绍。但是,要将所有内容集中在一处:

\n\n
\n

Py_DECREF(Py_None)是一件坏事……但是对于任何常数都是如此吗?

\n
\n\n

更一般的规则是,调用Py_DECREF任何您没有获得新的/被盗的引用并且没有调用的东西Py_INCREF都是一件坏事。由于您从不调用Py_INCREF任何可访问的常量,这意味着您从不调用Py_DECREF它们。

\n\n
\n

我是否必须减少由以下内容创建的任何内容的引用计数PyWhatever_New()

\n
\n\n

是的。任何返回“新引用”的内容都必须递减。按照惯例,任何以 结尾的内容都_New应该返回一个新的引用,但无论如何都应该记录它(例如,请参阅PyList_New)。

\n\n
\n

是否每个都Py_INCREF需要与 匹配Py_DECREF,还是应该有一个/另一个的多个?

\n
\n\n

你自己的代码中的数字不一定是平衡的。总数必须平衡,但 Python 本身内部会发生增量和减量例如,任何返回“新引用”的内容都已经执行了 inc,而任何“窃取”引用的内容都将对其执行 dec。

\n\n
\n

通过 C API 在堆栈上创建的 Python 对象是分配在堆栈上还是堆上?(例如,Py_INCREF 可能会在堆上重新分配它们)。

\n
\n\n

无法通过堆栈上的 C API 创建对象。C API 仅具有返回对象指针的函数。

\n\n

这些对象大部分都分配在堆上。有些实际上位于静态内存中。

\n\n

但无论如何你的代码不应该关心。您永远不会分配或删除它们;它们在 和类似的函数中分配PySpam_New,并在您将它们设置为 0 时自行释放Py_DECREF,因此它们在哪里对您来说并不重要。

\n\n

(例外的是您可以通过全局名称访问的常量,例如Py_None。您显然知道这些常量位于静态存储中。)

\n\n
\n

在将 C 代码中创建的 Python 对象传递给 Python 代码之前,我是否需要对其执行任何特殊操作?

\n
\n\n

不。

\n\n
\n

如果 Python 代码比创建 Python 对象的 C 代码寿命更长怎么办?

\n
\n\n

我不确定你所说的“寿命长”是什么意思。当任何对象依赖于其代码时,您的扩展模块将不会被卸载。(事实上​​,至少在 3.8 之前,您的模块在关闭之前可能永远不会被卸载。)

\n\n

如果您只是指返回一个对象的函数_New,那不是问题。您必须不遗余力地在堆栈上分配任何 Python 对象。如果不将 C 对象数组或 C 字符串等内容转换为 Python 对象元组或 Python 字节或字符串,则无法将它们传递到 Python 代码中。在某些情况下,例如,您可以将指向堆栈上某些内容的指针存储在 a 中PyCapsule并传递该 xe2x80x94 但这与任何 C 程序中的相同,并且 xe2x80xa6 只是不要这样做。

\n\n
\n

终于明白Python既有引用计数又有垃圾收集器了

\n
\n\n

垃圾收集器只是一个循环断路器。如果您的对象通过引用循环使彼此保持活动状态,则可以依靠 GC。但如果您泄漏了对某个对象的引用,GC 将永远不会清理它。

\n