内存管理 — Python 文档

来自菜鸟教程
Python/docs/2.7/c-api/memory
跳转至:导航、​搜索

内存管理

概述

Python 中的内存管理涉及一个包含所有 Python 对象和数据结构的私有堆。 这个私有堆的管理是由 Python 内存管理器 在内部确保的。 Python 内存管理器具有处理各种动态存储管理方面的不同组件,例如共享、分段、预分配或缓存。

在最低级别,原始内存分配器通过与操作系统的内存管理器交互,确保私有堆中有足够的空间来存储所有与 Python 相关的数据。 在原始内存分配器之上,几个特定于对象的分配器在同一个堆上运行,并实现适合每种对象类型特性的不同内存管理策略。 例如,整数对象在堆中的管理方式与字符串、元组或字典不同,因为整数意味着不同的存储要求和速度/空间权衡。 Python 内存管理器因此将一些工作委托给特定于对象的分配器,但确保后者在私有堆的范围内运行。

重要的是要理解 Python 堆的管理是由解释器本身执行的,用户无法控制它,即使他们定期操作指向该堆内内存块的对象指针也是如此。 Python 内存管理器通过本文档中列出的 Python/C API 函数按需为 Python 对象和其他内部缓冲区分配堆空间。

为避免内存损坏,扩展编写者不应尝试使用 C 库导出的函数对 Python 对象进行操作:malloc()calloc()realloc()free() ]。 这将导致 C 分配器和 Python 内存管理器之间的混合调用具有致命的后果,因为它们实现了不同的算法并在不同的堆上运行。 但是,可以出于个人目的使用 C 库分配器安全地分配和释放内存块,如以下示例所示:

PyObject *res;
char *buf = (char *) malloc(BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
...Do some I/O operation involving buf...
res = PyString_FromString(buf);
free(buf); /* malloc'ed */
return res;

在此示例中,I/O 缓冲区的内存请求由 C 库分配器处理。 Python 内存管理器仅参与作为结果返回的字符串对象的分配。

但是,在大多数情况下,建议专门从 Python 堆中分配内存,因为后者受 Python 内存管理器的控制。 例如,当用 C 编写的新对象类型扩展解释器时,这是必需的。 使用 Python 堆的另一个原因是希望 通知 Python 内存管理器关于扩展模块的内存需求。 即使请求的内存专门用于内部、高度特定的目的,将所有内存请求委托给 Python 内存管理器也会使解释器对整个内存占用有更准确的图像。 因此,在某些情况下,Python 内存管理器可能会也可能不会触发适当的操作,例如垃圾收集、内存压缩或其他预防程序。 请注意,通过使用前面示例中所示的 C 库分配器,为 I/O 缓冲区分配的内存完全转义了 Python 内存管理器。


内存接口

以下函数集以 ANSI C 标准为模型,但在请求零字节时指定行为,可用于从 Python 堆分配和释放内存:

void *PyMem_Malloc(size_t n)
分配 n 字节并返回类型为 void* 的指针到分配的内存,如果请求失败则返回 NULL。 如果可能,请求零字节将返回一个不同的非 NULL 指针,就好像 PyMem_Malloc(1) 已被调用一样。 内存不会以任何方式初始化。
void *PyMem_Realloc(void *p, size_t n)
p 指向的内存块大小调整为 n 字节。 内容将保持不变,以旧尺寸和新尺寸中的最小值为准。 如果pNULL,则调用等价于PyMem_Malloc(n); 否则如果 n 等于 0,内存块被调整大小但没有被释放,并且返回的指针是非 NULL。 除非 pNULL,否则它必须由先前对 PyMem_Malloc()PyMem_Realloc() 的调用返回。 如果请求失败,PyMem_Realloc() 返回 NULL 并且 p 仍然是指向前一个内存区域的有效指针。
void PyMem_Free(void *p)
释放 p 指向的内存块,该内存块必须由先前对 PyMem_Malloc()PyMem_Realloc() 的调用返回。 否则,或者如果 PyMem_Free(p) 之前已被调用,则会发生未定义的行为。 如果pNULL,则不执行任何操作。

为方便起见,提供了以下面向类型的宏。 请注意,TYPE 指的是任何 C 类型。

TYPE *PyMem_New(TYPE, size_t n)
PyMem_Malloc() 相同,但分配 (n * sizeof(TYPE)) 字节的内存。 返回一个指向 TYPE* 的指针。 内存不会以任何方式初始化。
TYPE *PyMem_Resize(void *p, TYPE, size_t n)
PyMem_Realloc() 相同,但内存块大小调整为 (n * sizeof(TYPE)) 字节。 返回一个指向 TYPE* 的指针。 返回时,p 将是一个指向新内存区域的指针,或者在失败的情况下 NULL。 这是一个 C 预处理器宏; p 总是被重新分配。 保存 p 的原始值,避免在处理错误时丢失内存。
void PyMem_Del(void *p)
PyMem_Free() 相同。

此外,还提供了以下宏集,用于直接调用 Python 内存分配器,不涉及上面列出的 C API 函数。 但是,请注意,它们的使用不会保持 Python 版本之间的二进制兼容性,因此在扩展模块中不推荐使用。

PyMem_MALLOC()PyMem_REALLOC()PyMem_FREE()

PyMem_NEW()PyMem_RESIZE()PyMem_DEL()


对象分配器

以下函数集以 ANSI C 标准为模型,但在请求零字节时指定行为,可用于从 Python 堆分配和释放内存。

默认情况下,这些函数使用 pymalloc 内存分配器

警告

使用这些功能时必须按住 GIL


void *PyObject_Malloc(size_t n)

分配 n 字节并返回类型为 void* 的指针到分配的内存,如果请求失败则返回 NULL

如果可能,请求零字节将返回一个不同的非 NULL 指针,就好像 PyObject_Malloc(1) 已被调用一样。 内存不会以任何方式初始化。

void *PyObject_Realloc(void *p, size_t n)

p 指向的内存块大小调整为 n 字节。 内容将保持不变,以旧尺寸和新尺寸中的最小值为准。

如果pNULL,则调用等价于PyObject_Malloc(n); 否则如果 n 等于 0,内存块被调整大小但没有被释放,并且返回的指针是非 NULL

除非 pNULL,否则它必须由先前对 PyObject_Malloc()PyObject_Realloc()PyObject_Calloc()

如果请求失败,PyObject_Realloc() 返回 NULL 并且 p 仍然是指向前一个内存区域的有效指针。

void PyObject_Free(void *p)

释放 p 指向的内存块,该内存块必须由先前对 PyObject_Malloc()PyObject_Realloc()PyObject_Calloc() 的调用返回]。 否则,或者如果 PyObject_Free(p) 之前已被调用,则会发生未定义的行为。

如果pNULL,则不执行任何操作。

此外,还提供了以下宏集:


pymalloc 分配器

Python 有一个 pymalloc 分配器,它针对生命周期较短的小对象(小于或等于 512 字节)进行了优化。 它使用称为“arenas”的内存映射,固定大小为 256 KiB。 对于大于 512 字节的分配,它回退到 malloc()realloc()

pymallocPyObject_Malloc() 的默认分配器。

arena 分配器使用以下函数:

  • mmap()munmap() 如果可用,
  • malloc()free() 否则。

2.7.7 版本变更:阈值从 256 字节变为 512 字节。 如果可用,竞技场分配器现在使用 mmap()


例子

这是 Overview 部分中的示例,重写后使用第一个函数集从 Python 堆分配 I/O 缓冲区:

PyObject *res;
char *buf = (char *) PyMem_Malloc(BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
/* ...Do some I/O operation involving buf... */
res = PyString_FromString(buf);
PyMem_Free(buf); /* allocated with PyMem_Malloc */
return res;

使用面向类型的函数集的相同代码:

PyObject *res;
char *buf = PyMem_New(char, BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
/* ...Do some I/O operation involving buf... */
res = PyString_FromString(buf);
PyMem_Del(buf); /* allocated with PyMem_New */
return res;

请注意,在上面的两个示例中,缓冲区始终通过属于同一组的函数进行操作。 实际上,需要为给定的内存块使用相同的内存 API 系列,以便将混合不同分配器的风险降至最低。 以下代码序列包含两个错误,其中一个被标记为 fatal,因为它混合了在不同堆上运行的两个不同分配器。

char *buf1 = PyMem_New(char, BUFSIZ);
char *buf2 = (char *) malloc(BUFSIZ);
char *buf3 = (char *) PyMem_Malloc(BUFSIZ);
...
PyMem_Del(buf3);  /* Wrong -- should be PyMem_Free() */
free(buf2);       /* Right -- allocated via malloc() */
free(buf1);       /* Fatal -- should be PyMem_Del()  */

除了旨在处理来自 Python 堆的原始内存块的函数之外,Python 中的对象通过 PyObject_New()PyObject_NewVar()PyObject_Del() 分配和释放

这些将在下一章关于在 C 中定义和实现新对象类型中进行解释。