Cython:已释放内存的内存视图

11
在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](它不返回原始数据)。

我的问题是:只释放指针总是安全的吗?内存视图对象是否会被通知已被释放的内存,或者我应该手动删除视图?另外,即使被内存视图引用,内存也有保证被释放了吗?

1个回答

7
需要深入挖掘C代码才能说明这一点,但是:
view = <double[:N]> ptr实际上生成了一个__pyx_array_obj。这与文档中详细介绍的"Cython array"和可导入为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 是不安全的。你似乎能够成功访问是运气好而已。只要不访问它,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被传递并且其生命周期难以跟踪,一种方法是手动创建一个设置了callback_free_datacython.view.array
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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接