处理C代码时,Python引用计数/垃圾收集有哪些注意事项?

6 投票
2 回答
2338 浏览
提问于 2025-04-15 23:19

为了好玩,我决定创建一个Scheme和libpython的绑定,这样你就可以在Scheme程序中嵌入Python了。我已经可以调用Python的C接口,但对内存管理还没仔细考虑过。

mzscheme的外部函数接口(FFI)工作方式是这样的:我可以调用一个函数,如果这个函数返回一个指向PyObject的指针,那么它可以自动增加引用计数。然后,我可以注册一个终结器,当Scheme对象被垃圾回收时,它会减少引用计数。我查看了关于引用计数的文档,初看上去没有什么问题(虽然在某些情况下可能不是最优的)。我有没有遗漏什么需要注意的地方呢?

另外,我在理解循环垃圾回收器文档时遇到了困难。我需要注意哪些事项呢?特别是,如何让Python知道我有一个对象的引用,这样它就不会在我还在使用它的时候把它回收掉?

2 个回答

3

我知道的关于引用计数和C语言接口的一个大问题就是__del__这个东西。当你借用一个对象的引用时,你可能会觉得不需要增加引用计数(INCREF),因为在使用这个引用的时候,你并没有放弃全局解释器锁(GIL)。但是,如果你最终删除了一个对象(比如,从列表中移除它),这可能会触发__del__方法的调用,而这个方法可能会在你借用的引用还在用的时候,把它给删掉。这就很棘手了。

如果你在拿到所有借用的引用后,立刻增加引用计数(INCREF),然后再减少引用计数(DECREF),那么就不会有问题了。

8

你提供的链接 http://docs.python.org/extending/extending.html#reference-counts 是个好地方。文档中的“扩展和嵌入”以及“Python/C API”部分会告诉你如何使用C API。

引用计数是使用C API时一个比较麻烦的部分。最主要的问题是搞清楚一切:根据你调用的API函数,你可能拥有你得到的对象的引用,也可能没有。你需要小心理解自己是否拥有这个引用(如果拥有,就不能忘记减少引用计数,或者把它给其他会“偷走”它的东西),或者是借用它(这时需要增加引用计数,以便在你的函数中保持使用)。最常见的错误有两个:1)错误地记住自己是否拥有某个函数返回的引用;2)错误地认为可以安全地借用一个引用的时间比实际更长。

对于循环垃圾回收器,你不需要做什么特别的事情。它只是用来修复引用计数中的一个缺陷,不需要直接访问。

撰写回答