在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]
(但它不会返回原始数据)。
我的问题是:只释放指针总是安全的吗?内存视图对象是否以某种方式通知内存被释放,或者我应该以某种方式手动删除该视图?此外,即使内存视图引用了内存,也能保证释放内存吗?
需要对C代码进行一些挖掘才能显示这一点,但是:
线CCD_ 4实际上产生一个CCD_。这与文档中详细描述的"Cython数组"类型相同,cimpportable与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代码生成的,所以如果你想进一步研究,它可能比生成的C.更容易读取
从本质上讲,内存视图包含一个cython.view.array
,它有一个指向原始数据的指针,但没有设置callback_free_data
。当内存视图失效时,将调用cython.view.array
的析构函数。这清理了一些内部,但并没有释放它所指向的数据(因为它没有指示如何释放)。
因此,在调用PyMem_Free
之后访问内存视图是不安全的。你似乎侥幸逃脱了惩罚。如果你不访问内存视图,那么内存视图保持现有是安全的
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_data
的cython.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
。