python - 什么时候 Py_INCREF?

标签 python memory-management cpython

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

到目前为止,通过反复试验,我了解到,例如,Py_DECREF(Py_None) 是一件坏事……但任何常量都是如此吗?我不知道。

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

  1. 如果 PyWhatever_New() 创建的任何东西的生命周期都没有超过创建它的过程,我是否必须减少它的引用计数?
  2. 是否每个 Py_INCREF 都需要与 Py_DECREF 匹配,还是应该有一个或另一个?
  3. 如果对 Python 过程的调用产生了 PyObject*,我是需要递增它以确保我仍然可以(永远)使用它,还是递减它以确保它最终会被垃圾收集,还是两者都不收集?
  4. 通过 C API 在栈上创建的 Python 对象是分配在栈上还是堆上? (例如,Py_INCREF 可能会在堆上重新分配它们)。
  5. 在将 C 代码创建的 Python 对象传递给 Python 代码之前,我是否需要对它们执行任何特殊操作?如果 Python 代码比创建 Python 对象的 C 代码生命周期长怎么办?
  6. 最后,我了解到 Python 同时具有引用计数和垃圾收集器:如果是这种情况,如果我弄乱了引用计数(即没有足够减少),GC 最终会弄清楚如何处理它们有多重要对象?

最佳答案

Reference Count Details 中涵盖了大部分内容,其余内容包含在有关您提出的具体问题的文档中。但是,要将所有内容集中在一个地方:

Py_DECREF(Py_None) is a bad thing... but is this true of any constant?

更一般的规则是,在任何你没有得到新的/被盗的引用并且没有调用 Py_INCREF 的东西上调用 Py_DECREF 是不好的事物。由于您永远不会对任何可作为常量访问的内容调用 Py_INCREF,这意味着您永远不会对它们调用 Py_DECREF

Do I have to decrement refcount on anything created by PyWhatever_New()

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

Does every Py_INCREF need to be matched by Py_DECREF, or should there be one more of one / the other?

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

Are Python objects created through C API on the stack allocated on stack or on heap? (It is possible that Py_INCREF reallocates them on heap for example).

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

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

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

(除了常量,您可以通过它们的全局名称访问它们,例如 Py_None。您显然知道它们在静态存储中。)

Do I need to do anything special to Python objects created in C code before passing them to Python code?

没有。

What if Python code outlives C code that created Python objects?

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

如果您只是指 _New 返回一个对象的函数,那不是问题。您必须竭尽全力在堆栈上分配任何 Python 对象。如果不将对象的 Python 元组或 Python 字节或 str 转换为 Python 对象元组,则无法将 C 对象数组或 C 字符串之类的东西传递给 Python 代码。在某些情况下,例如,您可以在 PyCapsule 中存储指向堆栈中某物的指针并传递它——但这与任何 C 程序中的相同,并且……只是不要这样做

Finally, I understand that Python has both reference counting and garbage collector

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

关于python - 什么时候 Py_INCREF?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50336978/

相关文章:

python - 将Python程序转换为C : How can I multiply a character by a specified value and store it into a variable?

python - 动态检测部门 future 进口

c++ - 代码块内存限制

python - CPython 中的字符串乘法是如何实现的?

python - 为什么将我的模块分成多个文件会使它变慢?

python - 在 C++ 应用程序中嵌入 python 环境

python - 在 Linux Ubuntu 上无法打开 Scrapy

python - 如何从 HTML 文件中提取标签并将其写入新文件?

c - 使用对 malloc 的单次调用为 C 89/90 中未知数据类型的二维数组动态分配连续的内存块

C 内存管理问题