Ich analysierte den folgenden Code, der korrekt kompiliert und ausgeführt wird, aber ein Speicherleck generiert. Die cfiboheap
ist eine C-Implementierung eines Fibonacci Heap und der folgende Code ist ein Cython Wrapper (ein Teil davon) für cfiboheap
.Verwirrendes Referenzeigentum: wie man (über Py_DECREF) Objekte eines Gegenstandes richtig freigibt?
Meine Zweifel beginnt auf der Einfügefunktion. Das Objekt data
wurde irgendwo angelegt und an die Funktion insert()
übergeben. Da die Funktion dieses Objekt zum Fiboheap hinzufügen möchte, erhöht sie ihre Referenzzahl. Aber danach? An wen geht das Eigentum? In meinem Verständnis leiht die C-Funktion fh_insertkey()
nur das Eigentum. Dann gibt es einen proprietären Zeiger zurück, der eingekapselt werden muss, und wird dann von insert()
zurückgegeben. Cool. Aber mein Objekt data
und seine Ref zählen? Durch das Erstellen der Kapsel erstelle ich ein neues Objekt, aber ich verringere nicht die Ref-Anzahl von data
. Dies erzeugt das Speicherleck.
(Beachten Sie, dass Auskommentierung Py_INCREF
oder das Hinzufügen von Py_DECREF
vor der Rückkehr insert()
Ergebnisse in einem Segmentierungsfehler.)
Meine Fragen sind:
1) Warum ist es notwendig, den Verweiszähler von data
zu erhöhen während der insert()
?
2) Warum ist es nicht notwendig, einen Py_DECREF
während der extract()
zu verwenden?
3) Wie kann ich den Referenzbesitz beim Springen zwischen C und Python genauer verfolgen?
4) Wie man ein Objekt wie dieses FiboHeap richtig freigibt? Sollte ich präventiv eine Py_XDECREF
in __dealloc__()
verwenden und wenn ja, wie?
Danke!
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
Beachten Sie, dass diese von mir geschrieben wurde nicht, aber ich bin mit diesem Beispiel besser zu verstehen obj Referenzierung und das Leck zu stoppen (da ich eigentlich den Code verwende).
Der Hauptcode, der Verwendung von FiboHeap
(und wo das Leck passiert) sieht wie folgt aus macht:
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
kein spähen, sondern eine richtige Pop, so dass er das C-Element in der löscht C Fibohap.
Fazit: Es scheint klar, dass die ref count von data
einen Speicherverlust verursacht, aber warum? Und wie man es stoppt?
Die erste (aber andere) Frage zu diesem Speicherleck finden Sie [hier] (http://stackoverflow.com/questions/38251216/how-to-deallocate-a-typed-numpy-array-is-setting -callback-free-Daten-a-lebensfähige-op). – Gioker
Warum machen Sie überhaupt eine Kapsel? Es scheint nutzlos und unsicher. Macht 'extract' auch einen Peek oder Pop? – user2357112
Ich poste hier, da du es in deiner ersten Frage erwähnt hast - ich glaube nicht, dass ich verstehe, was "fiboheap" gut genug macht, um wirklich darauf zu antworten. Es würde vermeiden, 'PyObject *' s und Referenzzählung in Cython zu verwenden - es ist sehr schwer, richtig zu machen. Die Frage @ user2357112 gefragt, was 'extract' tut ist hier der Schlüssel .... – DavidW