Python 如何取消分配类型化numpy数组?设置回调\u自由\u数据是否可行?
在使用开源Cython库时,我发现内存泄漏。泄漏似乎来自一个类型化的numpy数组,当它超出范围时,它不会从内存中释放出来。声明如下:Python 如何取消分配类型化numpy数组?设置回调\u自由\u数据是否可行?,python,numpy,memory-leaks,cython,Python,Numpy,Memory Leaks,Cython,在使用开源Cython库时,我发现内存泄漏。泄漏似乎来自一个类型化的numpy数组,当它超出范围时,它不会从内存中释放出来。声明如下: cdef np.ndarray[object, ndim=1] my_array = np.empty(my_size, dtype=object) 在我的理解中,垃圾收集器应该像其他任何numpy数组一样考虑这一点,并且GC应该在数组超出范围时立即释放内存——在本例中,是在声明它的函数的末尾。显然,这不会发生 如果首先使用cython数组创建数组,然后将其强
cdef np.ndarray[object, ndim=1] my_array = np.empty(my_size, dtype=object)
在我的理解中,垃圾收集器应该像其他任何numpy数组一样考虑这一点,并且GC应该在数组超出范围时立即释放内存——在本例中,是在声明它的函数的末尾。显然,这不会发生
如果首先使用cython数组创建数组,然后将其强制转换为numpy数组,则可以使用callback_free_data函数,如和所述。但是,在这种情况下,无法到达my_array
的指针,也无法设置回调
你知道为什么这种声明会导致内存泄漏和/或如何强制解除分配吗
更新:
我的问题非常笼统,我想避免发布代码,因为它有点复杂,但既然有人问我们:
cdef dijkstra(Graph G, int start_idx, int end_idx):
# Some code
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)
# Some other code where it could perform operations like:
# Q.decrease_key(fiboheap_nodes[w], vw_distance)
# End of operations
# do we need to cleanup the fiboheap_nodes array here?
return
FiboHeap
是c实现的Cython包装器。例如,insert函数如下所示:
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)
cimport cfiboheap
从cpython.pycapsule cimport pycapsule\u New,pycapsule\u GetPointer
从python\u ref cimport Py\u INCREF,Py\u DECREF
cdef内联对象将_fibheap_el_转换为_pycapsule(cfiboheap.fibheap_el*元素):
返回新的(元素,NULL,NULL)
cdef类纤维EAP:
定义(光纤EAP自身):
self.treeptr=cfiboheap.fh_makeyheap()
如果self.treeptr为空:
提高内存错误()
def uu dealloc uu(光纤EAP自身):
如果self.treeptr不为空:
cfiboheap.fh_deleteheap(self.treeptr)
cpdef对象插入(FiboHeap self,双键,对象数据=无):
Py_增量(数据)
cdef cfiboheap.fibheap_el*retValue=cfiboheap.fh_insertkey(self.treepter,key,data)
如果retValue为空:
提高内存错误()
返回convert_fibheap_el_to_pycapsule(retValue)
\uuuu dealloc\uuuu()
函数按预期工作,因此FiboHeap在函数dijkstra(…)
结束时从内存中释放。我的猜测是fiboheap_节点中包含的指针出了问题。
有什么猜测吗?问题(在评论中已解决)不是numpy数组的释放。相反,numpy数组持有一组Fiboheap
对象,这些对象本身持有指向一组Python对象的指针。是这些对象没有被释放
当获取Fiboheap
中的Python对象指针时(在insert
中),它们的引用计数会增加,以确保它们保持活动状态。但是,当Fiboheap
被销毁时(在\uuuu dealloc\uuuu
中),它所持有的Python对象的引用计数没有减少,从而导致内存泄漏。解决方案是确保在\uuuu dealloc\uuuu
期间对所有保留的Python对象调用Py\u DECREF
可能还有第二个更具挑战性的问题等待出现:由
Fiboheap
持有的对象本身可能包含对Fiboheap
的引用,可能是间接的。Python使用函数查找这些循环,并tp_clear
将其中断。Cython将自动为其cdef
类生成tp_遍历
,但是由于它无法知道隐藏在CFiboheap
结构中的Python对象指针,因此无法正确处理这些指针(可能会生成另一个内存泄漏)
这在现实中可能不太可能发生,因此可能不值得担心,但需要注意。描述了一种在Cython中生成自定义
tp\u遍历
函数的方法。对于大多数应用程序来说,这是不必要的-只有Cythonobject
和PyObject*
的混合使得它在这里稍微成为可能。它确实应该被释放。到目前为止,最有可能的解释是,对它的引用一直保存在其他地方。考虑到它是一个对象
数组,它包含对自身的循环引用并非完全不可能(在这种情况下,调用gc.collect()
可能会起作用)。除此之外,您可能需要发布代码(或指向相关开源库的指针)以获得有用的答案(最好是以最小的完整形式)。或者至少是更详细的描述-这是一个全局变量、一个局部函数变量、一个类的成员…等等?我已经尝试了gc.collect()
,但它不起作用。在这种奇怪的情况下(包含对象指针的数组),gc的行为不确定(请参阅)。我的第一个想法是,这可能与tp_traverse
(垃圾收集器用于查找周期的函数)有关-Cython显然会自动生成它,但它不会知道Cfiboheap
中包含的指针。我找到了这个链接,但实际上做起来似乎很困难。。。(我可能稍后会考虑)另一个想法-在代码中,您显示了当解除分配FiboHeap
时,对象的引用没有减少。如果代码完成,则堆上存储的对象肯定存在内存泄漏。