PyBuffer_New: 需要手动释放吗?

PyBuffer_New: Do I need to free manually?

我正在寻找其他人代码中的内存泄漏。我发现:

def current(self):
    ...
    data = PyBuffer_New(buflen)
    PyObject_AsCharBuffer(data, &data_ptr, &buflen)
    ...
    return VideoFrame(data, self.frame_size, self.frame_mode,
                      timestamp=<double>self.frame.pts/<double>AV_TIME_BASE,
                      frameno=self.frame.display_picture_number)


cdef class VideoFrame:
    def __init__(self, data, size, mode, timestamp=0, frameno=0):
        self.data = data
        ...

在函数 current() 中没有 free 或类似的,在 VideoFrame 中也没有。当 VideoFrame 对象被删除时 PyBuffer 是否自动释放?

答案是:"it depends; we don't have enough code to tell from your question." 它取决于您告诉 Cython PyBuffer_New return 的类型。我将给出两个简化的说明性案例,希望您能够解决更复杂的案例。

如果你告诉 Cython 它是一个 PyObject* 它没有那种类型的内在知识,并且不会做任何事情来跟踪内存:

# BAD - memory leak!

cdef extern from "Python.h":
    ctypedef struct PyObject
    PyObject* PyBuffer_New(int size)

def test():
    cdef int i
    for i in range(100000): # call lots of times to allocate lots of memory
        # (type of a is automatically inferred to be PyObject*
        # to match the function definition)
        a = PyBuffer_New(1000)

并且为循环生成的代码非常类似于:

for (__pyx_t_1 = 0; __pyx_t_1 < 1000; __pyx_t_1+=1) {
  __pyx_v_i = __pyx_t_1;
  __pyx_v_a = PyBuffer_New(1000);
}

即正在分配内存但从未释放。如果你 运行 test() 并查看任务管理器,你可以看到内存使用率上升而不是 return。

或者,如果您告诉 Cython 它是一个 object 让 Cython 像处理任何其他 Python 对象一样处理它,并正确管理引用计数:

# Good - no memory leak

cdef extern from "Python.h":
    object PyBuffer_New(int size)

def test():
    cdef int i
    for i in range(100000):# call lots of times to allocate lots of memory
        a = PyBuffer_New(1000)

循环生成的代码是

for (__pyx_t_1 = 0; __pyx_t_1 < 100000; __pyx_t_1+=1) {
  __pyx_v_i = __pyx_t_1;
  __pyx_t_2 = PyBuffer_New(1000); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 7; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_2);
    __Pyx_XDECREF_SET(__pyx_v_a, __pyx_t_2);
    __pyx_t_2 = 0;
}

注意 DECREF,这将是对象被释放的地方。如果你 运行 test() 在这里你看不到内存使用量的长期跳跃。

通过对变量使用 cdef 可以在这两种情况之间跳转(例如在 VideoFrame 的定义中)。如果他们使用 PyObject* 时不小心 DECREFs 那么他们可能正在泄漏内存...