Python 如何取消分配类型化numpy数组?设置回调\u自由\u数据是否可行?

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数组创建数组,然后将其强

在使用开源Cython库时,我发现内存泄漏。泄漏似乎来自一个类型化的numpy数组,当它超出范围时,它不会从内存中释放出来。声明如下:

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_遍历
,但是由于它无法知道隐藏在C
Fiboheap
结构中的Python对象指针,因此无法正确处理这些指针(可能会生成另一个内存泄漏)


这在现实中可能不太可能发生,因此可能不值得担心,但需要注意。描述了一种在Cython中生成自定义
tp\u遍历
函数的方法。对于大多数应用程序来说,这是不必要的-只有Cython
object
PyObject*
的混合使得它在这里稍微成为可能。

它确实应该被释放。到目前为止,最有可能的解释是,对它的引用一直保存在其他地方。考虑到它是一个
对象
数组,它包含对自身的循环引用并非完全不可能(在这种情况下,调用
gc.collect()
可能会起作用)。除此之外,您可能需要发布代码(或指向相关开源库的指针)以获得有用的答案(最好是以最小的完整形式)。或者至少是更详细的描述-这是一个全局变量、一个局部函数变量、一个类的成员…等等?我已经尝试了
gc.collect()
,但它不起作用。在这种奇怪的情况下(包含对象指针的数组),gc的行为不确定(请参阅)。我的第一个想法是,这可能与
tp_traverse
(垃圾收集器用于查找周期的函数)有关-Cython显然会自动生成它,但它不会知道C
fiboheap
中包含的指针。我找到了这个链接,但实际上做起来似乎很困难。。。(我可能稍后会考虑)另一个想法-在代码中,您显示了当解除分配
FiboHeap
时,对象的引用没有减少。如果代码完成,则堆上存储的对象肯定存在内存泄漏。