动态内存分配在Python中基本上不是问题。每个东西都是一个对象,引用计数系统和垃圾收集器在不再使用时自动将内存返回给系统。
当涉及到更底层的数据缓冲区时,Cython通过NumPy、内存视图或Python的stdlib数组类型对简单类型的(多维)数组提供了特殊的支持。它们功能齐全,支持垃圾回收,并且比C语言中的纯指针更容易使用,同时仍然保持了速度和静态类型的优点。参见使用Python数组和类型化内存视图。
然而,在某些情况下,这些对象仍然会产生不可接受的开销,这就需要在C语言中进行手动内存管理。
简单的C值和结构(例如局部变量cdef double x)通常在堆栈上分配并通过值传递,但是对于更大更复杂的对象(例如动态大小的double列表),必须手动请求并释放内存。C为此提供了malloc()、realloc()和free()函数,这些函数可以从clibc.stdlib导入到cython中。他们的签名是:
void* malloc(size_t size)
void* realloc(void* ptr, size_t size)
void free(void* ptr)下面是使用malloc的一个非常简单的例子:
import random
from libc.stdlib cimport malloc, free
def random_noise(int number=1):
cdef int i
# 分配 number * sizeof(double) 字节的内存
cdef double *my_array = <double *> malloc(number * sizeof(double))
if not my_array:
raise MemoryError()
try:
ran = random.normalvariate
for i in range(number):
my_array[i] = ran(0, 1)
# ... 让我们假设我们在这里做了一些更复杂的C计算,
# 以弥补在丢弃上面的现有对象之后将下面的C double值
# 打包到Python float对象中所做的工作。
return [x for x in my_array[:number]]
finally:
# 返回预先分配的内存到系统
free(my_array)注意,用于在Python堆上分配内存的C-API函数通常比上面的底层C函数更受欢迎,因为它们提供的内存实际上是在Python的内部内存管理系统中计算的。它们还对较小的内存块进行了特殊的优化,从而避免了昂贵的操作系统调用,加快了内存块的分配。
C-API函数可以在cpython.mem标准声明文件中找到:
from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free它们的接口和用法与相对应的底层C函数相同。
需要记住的一件重要事情是,使用malloc()或PyMem_Malloc()获得的内存块必须在不再使用时手动释放,并调用相应的free()或PyMem_Free()(并且必须始终使用匹配类型的free函数)。否则,直到python进程退出,它们才会被回收。这被称为内存泄漏。
如果一个内存块需要比管理它的try..finally块更长的生命周期,另一个有用的习惯用法是将它的生命周期绑定到Python对象上,以利用Python运行时的内存管理,例如:
If a chunk of memory needs a larger lifetime than can be managed by a try..finally block, another helpful idiom is to tie its lifetime to a Python object to leverage the Python runtime’s memory management, e.g.:
from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
cdef class SomeMemory:
cdef double* data
def __cinit__(self, size_t number):
# 分配一些内存(不初始化,可能含有任意值)
self.data = <double*> PyMem_Malloc(number * sizeof(double))
if not self.data:
raise MemoryError()
def resize(self, size_t new_number):
# 分配 new_number * sizeof(double) 个字节,
# 保留现有内容,并尽最大可能重复利用原始数据位置。
mem = <double*> PyMem_Realloc(self.data, new_number * sizeof(double))
if not mem:
raise MemoryError()
# 只在内存确实被重新分配后覆盖指针。Only overwrite the pointer if the memory was really reallocated.
# 若出现错误(mem is NULL),原始的内存并未被释放。
self.data = mem
def __dealloc__(self):
PyMem_Free(self.data) # 若self.data为NULL则无操作