Сбивание справочной собственности: как правильно освободить (через Py_DECREF) объекты объекта?

Я анализировал следующий код, который компилируется и работает правильно, но генерирует утечку памяти.

cfiboheap является реализацией C кучи Фибоначчи, а следующий код представляет собой оболочку Cython (ее часть) для cfiboheap .

Мои сомнения начинаются с функции вставки. Данные объекта были созданы где-то и переданы функции insert() . Так как функция хочет добавить этот объект к фибохиру, он увеличивает счетчик ссылок. Но потом? Кому принадлежит право собственности? По моему fh_insertkey() функция C fh_insertkey() просто заимствует право собственности. Затем он возвращает проприетарный указатель, который должен быть инкапсулирован, а затем возвращен insert() . Круто. Но мои data объекта и его ref count? Создав капсулу, я создаю новый объект, но я не уменьшаю количество ссылок на data . Это вызывает утечку памяти.

(Обратите внимание, что комментирование Py_INCREF или добавление Py_DECREF перед возвратом insert() приводит к ошибке сегментации.)

Мои вопросы:

1) Почему необходимо увеличивать количество ссылок на data во время insert() ?

2) Почему нет необходимости использовать Py_DECREF во время extract() ?

3) В более общем плане, как я могу точно отслеживать правовое владение при переходе между C и Python?

4) Как правильно освободить объект, подобный этому FiboHeap? Должен ли я использовать превентивно Py_XDECREF в __dealloc__() и, если да, как?

Благодаря!

 cimport cfiboheap from cpython.pycapsule cimport PyCapsule_New, PyCapsule_GetPointer from python_ref cimport Py_INCREF, Py_DECREF cdef inline object convert_fibheap_el_to_pycapsule(cfiboheap.fibheap_el* element): return PyCapsule_New(element, NULL, NULL) cdef class FiboHeap: def __cinit__(FiboHeap self): self.treeptr = cfiboheap.fh_makekeyheap() if self.treeptr is NULL: raise MemoryError() def __dealloc__(FiboHeap self): if self.treeptr is not NULL: cfiboheap.fh_deleteheap(self.treeptr) cpdef object insert(FiboHeap self, double key, object data=None): Py_INCREF(data) cdef cfiboheap.fibheap_el* retValue = cfiboheap.fh_insertkey(self.treeptr, key, <void*>data) if retValue is NULL: raise MemoryError() return convert_fibheap_el_to_pycapsule(retValue) cpdef object extract(FiboHeap self): cdef void* ret = cfiboheap.fh_extractmin(self.treeptr) if ret is NULL: raise IndexError("FiboHeap is empty") return <object> ret cpdef object decrease_key(FiboHeap self, object element, double newKey): cdef void* ret = cfiboheap.fh_replacekey(self.treeptr, convert_pycapsule_to_fibheap_el(element), newKey) if ret is NULL: raise IndexError("New Key is Bigger") return <object> ret 

Обратите внимание, что это не было написано мной, но я использую этот пример, чтобы лучше понять ссылку obj и остановить утечку (поскольку я фактически использую код).

Основной код, который использует FiboHeap (и где происходит утечка) выглядит следующим образом:

 cdef dijkstra(Graph G, int start_idx, int end_idx): cdef np.ndarray[object, ndim=1] fiboheap_nodes = np.empty([G.num_nodes], dtype=object) # holds all of our FiboHeap Nodes Pointers Q = FiboHeap() fiboheap_nodes[start_idx] = Q.insert(0, start_idx) # Then occasionally: Q.insert(...) Q.decrease_key(...) Q.extract() return 

extract – это не быстрый взгляд, а правильный поп, поэтому он удаляет элемент C в фибохебе C.

В заключение: кажется очевидным, что количество обращений к data вызывает утечку памяти, но почему? И как это остановить?

One Solution collect form web for “Сбивание справочной собственности: как правильно освободить (через Py_DECREF) объекты объекта?”

1) Необходимо увеличить счетчик ссылок во insert потому что счетчик ссылок будет автоматически уменьшен в конце вставки. Cython не знает, что вы храните объект позже. (Вы можете проверить сгенерированный код C, чтобы увидеть DECREF в конце функции). Если insert вызывается с объектом отсчета 1 (например, .insert(SomeObject()) , тогда объект будет уничтожен в конце вставки без INCREF

2) Если объект удаляется из cfiboheap во время extract вы должны сделать DECREF чтобы подтвердить тот факт, что вы больше не удерживаете его. Сначала запустите его для объекта (так что вы все еще держите ссылку на него)

  cdef void* ret = cfiboheap.fh_extractmin(self.treeptr) # refcount is 1 here (from the INCREF when it was stored) if ret==NULL: # ... ret_obj = <object>ret # reference count should be 2 here - one for being on the heap and one for ret_obj. Casting to object increases the refcount in Cython Py_DECREF(ret_obj) # 1 here return ret_obj 

3) Честно говоря, вы пытаетесь не использовать PyObject* если можете этого избежать! Гораздо лучше позволить Китону выполнить эту работу. Если вы не можете этого избежать, вам просто нужно убедиться, что INCREF вызывается один раз при сохранении объекта, а DECREF вызывается один раз, когда вы прекращаете его хранение.

4) Вам нужно уменьшить остальные объекты в куче в __dealloc__ . Очень простой способ сделать это может быть для всех extract пока cfiboheap будет пустым:

 try: while True: self.extract() except IndexError: pass # ignore the error - we're done emptying the heap 

Комментарий об использовании капсул: кому принадлежит fibheap_el который они указывают (и когда это разрушается)? Если он разрушается, когда cfiboheap становится разрушенным, возникает проблема с капсулой с еще недействительным указателем. Использование этой капсулы где-то может привести к проблемам. Если он не будет разрушен cfiboheap тогда у вас может возникнуть другая утечка памяти.

  • Словарь Python vs C ++ std: unordered_map (cython) vs cythonized python dict
  • Передача указателя numpy (dtype = np.bool) на C ++
  • как перезагрузить модуль cython в интерактивном режиме с помощью pyximport
  • Оптимизация NumPy с Cython
  • Wrap C ++ lib с Cython
  • Cython и fortran - как скомпилировать без f2py
  • Класс расширения Cython: как мне разоблачить методы в автоматически сгенерированной структуре C?
  • Cython-Can not open include file: 'io.h': нет такого файла или каталога
  • Python - лучший язык программирования в мире.