首页 > 解决方案 > 使用回调将 Python 对象传递给 C 时如何避免内存泄漏?

问题描述

为了避免下面描述的内存泄漏,我想直接从 Python 调用 Py_DecRef。有没有办法做到这一点?这个问题有更好的解决方案吗?

我正在使用 ctypes 将我的 Python 代码连接到我没有代码的 C 库。C 库不是为 Python 编写的,因此它对 Python 对象一无所知。

C 库使用两个回调:第一个创建一个对象并返回一个指向它的 void* 指针,第二个获取指针作为参数并应该销毁它。在 C 头文件中,这些回调函数的类型定义如下:

typedef void* (*CreateCallback)();
typedef void (*DestroyCallback)(void*);

这些回调可以在 Python 中定义,如下所示(简化代码)。如注释中所述,当前代码存在内存泄漏。

import ctypes

CreateCallback = ctypes.CFUNCTYPE(ctypes.py_object)
DestroyCallback = ctypes.CFUNCTYPE(None, ctypes.py_object)

class Object:
   pass # In the real application, this contains more code

@CreateCallback
def create():
   return Object()
   # Ctypes correctly increments the reference count of the 
   # object, to make sure it does not get garbage collected
   # while the C code holds a reference to it.

@DestroyCallback
def destroy(object):
   pass
   # Above, the reference count of the object should be
   # decremented, because the C code no longer holds a
   # reference to it. However, Ctypes does not know this so 
   # cannot do it automatically. How can I do this from
   # Python? Is it possible to call Py_DecRef or similar
   # directly from Python?

一种选择是创建一个调用 Py_DecRef 的 C 函数,将该 C 函数编译成一个 dll(对于 Linux 来说是这样),然后从上面的 destroy 函数中调用它。该解决方案至少有两个缺点:

  1. 只为一个函数创建一个 dll 似乎过于复杂

  2. C 代码必须针对特定版本的 Python 进行编译,而不是使用运行我的 Python 代码的任何版本的 Python。请注意,我需要它在 Windows 上工作,其中 dll 不能包含未定义的全局变量。

标签: pythoncmemory-leakscallbackctypes

解决方案


推荐阅读