【问题标题】:Cython: Memory view of freed memoryCython:释放内存的内存视图
【发布时间】:2016-07-15 20:56:03
【问题描述】:

在 Cython 代码中,我可以分配一些内存并将其包装在内存视图中,例如像这样:

cdef double* ptr
cdef double[::1] view
ptr = <double*> PyMem_Malloc(N*sizeof('double'))
view = <double[:N]> ptr

如果我现在使用PyMem_Free(ptr) 释放内存,尝试访问ptr[i] 之类的元素会引发错误,这是应该的。不过,我可以安全地尝试访问view[i](虽然它不会返回原始数据)。

我的问题是:释放指针总是安全的吗?内存视图对象是否以某种方式通知内存被释放,或者我应该以某种方式手动删除视图?此外,即使内存视图引用了内存,是否也能保证释放内存?

【问题讨论】:

    标签: python memory cython free memoryview


    【解决方案1】:

    需要深入研究 C 代码才能显示这一点,但是:

    view = &lt;double[:N]&gt; ptr 行实际上生成了一个__pyx_array_obj。这与the documentation as a "Cython array" and cimportable as cython.view.array 中详述的类型相同。 Cython 数组确实有一个名为 callback_free_data 的可选成员,它可以充当析构函数。

    该行翻译为:

    struct __pyx_array_obj *__pyx_t_1 = NULL;
    # ...
    __pyx_t_1 = __pyx_array_new(__pyx_t_2, sizeof(double), PyBytes_AS_STRING(__pyx_t_3), (char *) "c", (char *) __pyx_v_ptr);
    

    __pyx_t_2__pyx_t_3 只是分别存储大小和格式的临时变量)。如果我们查看__pyx_array_new 内部,我们首先会看到数组的data 成员直接分配给作为__pyx_v_ptr 传递的值

    __pyx_v_result->data = __pyx_v_buf;
    

    (即制作副本),其次,未设置callback_free_data旁注:cython.view.array 的 C 代码实际上是从 Cython code 生成的,因此如果您想进一步调查,它可能比生成的 C 更容易阅读。


    基本上,memoryview 拥有一个cython.view.array,它有一个指向原始数据的指针,但没有设置callback_free_data。当 memoryview 死亡时,cython.view.array 的析构函数被调用。这会清理一些内部结构,但不会释放它指向的数据(因为它没有指示如何这样做)。

    因此,在您调用PyMem_Free 之后访问内存视图是不安全的。你似乎侥幸逃脱的事实是运气。但是,如果您不访问它,memoryview 保持存在是安全的。像这样的函数:

    def good():
        cdef double* ptr
        cdef double[::1] view
        ptr = <double*> PyMem_Malloc(N*sizeof('double'))
        try:
            view = <double[:N]> ptr
            # some other stuff
        finally:
            PyMem_Free(ptr)
        # some other stuff not involving ptr or view
    

    会好的。像这样的函数:

    def bad():
        cdef double* ptr
        cdef double[::1] view
        ptr = <double*> PyMem_Malloc(N*sizeof('double'))
        try:
            view = <double[:N]> ptr
            # some other stuff
        finally:
            PyMem_Free(ptr)
        view[0] = 0
        return view
    

    这不是一个好主意,因为它传回了一个不指向任何东西的内存视图,并在它查看的数据被释放后访问view

    你一定要确保在某个时候调用PyMem_Free,否则你有内存泄漏。如果view 被传递,因此很难跟踪生命周期,一种方法是手动创建一个cython.view.array 并设置callback_free_data

    cdef view.array my_array = view.array((N,), allocate_buffer=False)
    my_array.data = <char *> ptr
    my_array.callback_free_data = PyMem_Free
    view = my_array
    

    如果view 的生命周期很明显,那么您可以像以前一样在ptr 上调用PyMem_Free

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-18
      • 2011-05-10
      • 2015-06-21
      相关资源
      最近更新 更多