PyBuffer_New: мне нужно бесплатно вручную?

Я ищу утечку памяти в коде кого-то еще. Я нашел:

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, Это PyBuffer автоматически освобождается, когда VideoFrame объект удаляется?

1 ответ

Решение

Ответ таков: "это зависит; у нас недостаточно кода, чтобы ответить на ваш вопрос". Это зависит от того, какой тип вы сказали Cython, что PyBuffer_New возвращается. Я приведу два упрощенных иллюстрирующих случая, и, надеюсь, вы сможете решить их для более сложного случая.

Если вы скажете 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() и посмотрите на диспетчер задач, вы можете увидеть, как использование памяти увеличивается, а не возвращается.

В качестве альтернативы, если вы скажете 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* без осторожного DECREFтогда у них, вероятно, утечка памяти...

Другие вопросы по тегам