需要深入研究 C 代码才能显示这一点,但是:
view = <double[:N]> 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。