它需要深入研究 C 代码才能显示这一点,但是:
该行view = <double[:N]> ptr
实际上生成一个__pyx_array_obj
. 这与文档中详述的“Cython 数组”和 cimportable ascython.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
是没有设置。旁注: C 代码cython.view.array
实际上是从Cython 代码生成的,因此如果您想进一步调查,它可能比生成的 C 更容易阅读。
本质上,memoryview 拥有cython.view.array
一个指向原始数据的指针,但没有callback_free_data
设置。当 memoryview 死掉的析构函数cython.view.array
被调用。这清理了一些内部结构,但对释放它指向的数据没有任何作用(因为它没有指示如何这样做)。
因此,在调用后访问 memoryview是不安全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
with callback_free_data
set:
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
您可以照常打电话PyMem_Free
。ptr