コンパイルして正しく実行するが、メモリリークを生成する以下のコードを解析していました。参照所有権の混乱:オブジェクトのオブジェクトを(Py_DECREF経由で)適切に割り当て解除する方法はありますか?
cfiboheap
はフィボナッチヒープのC実装であり、次のコードはcfiboheap
のCythonラッパー(一部)です。
私の疑義は挿入機能から始まります。オブジェクトdata
はどこかで作成され、関数insert()
に渡されました。関数はこのオブジェクトをファイバーヒープに追加したいので、参照カウントを増加させます。しかしその後?所有権は誰に行くのですか?私の理解では、C関数fh_insertkey()
は所有権を借りるだけです。次に、カプセル化が必要なプロプライエタリポインタを返し、によって返します。クール。しかし、私のオブジェクトdata
とそのrefカウント?カプセルを作成することで新しいオブジェクトを作成していますが、参照カウントをdata
に減らすことはありません。これにより、メモリリークが発生します。
(Py_INCREF
をコメントアウトするか、セグメンテーションフォールトでinsert()
結果の返却前にPy_DECREF
を追加することに注意してください。)
私の質問は以下のとおりです。
1)なぜそれがdata
の参照カウントをインクリメントする必要がありますinsert()
の間に?
2)なぜextract()
の間にPy_DECREF
を使用する必要はありませんか?
3)より一般的には、CとPythonの間でジャンプするときの参照所有権を正確に追跡するにはどうすればよいですか?
4)このFiboHeapのようなオブジェクトの割り当てを正しく解除するにはどうすればいいですか? __dealloc__()
に予防的にPy_XDECREF
を使用する必要があります。
ありがとうございます!
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
結論:refカウントがdata
であることはメモリリークを引き起こすことは明らかですが、なぜですか?それをやめる方法は?
このメモリリークについての最初の(しかし異なる)質問が見つかりました[ここ](http://stackoverflow.com/questions/38251216/how-to-deallocate-a-typed-numpy-array-is-setting -callback-free-data-a-viable-op)を使用します。 – Gioker
なぜあなたはカプセルをまったく作っていますか?それは役に立たず、安全ではないようです。また、「抽出する」は、ピークまたはポップを行いますか? – user2357112
あなたが最初の質問でそれを言及して以来、私はここに投稿しています - 私は 'fiboheap'が本当にこれに答えるのに十分なことを理解しているとは思いません。 Cythonで 'PyObject *'と参照カウントを使用しようとするのを避けることができます。 @ user2357112が 'extract 'とは何かについて質問したところ、ここにはキーがあります.... – DavidW