python / 3.7.2rc1 / all / c-api-memory.html

Memory Management

Overview

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

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

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

为避免内存损坏,扩展编写程序切勿try使用 C 库所导出的函数malloc()calloc()realloc()free()对 Python 对象进行操作。这将导致 C 分配器和 Python 内存 Management 器之间的混合调用产生致命的后果,因为它们实现不同的算法并在不同的堆上运行。但是,可以出于个人目的使用 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 = PyBytes_FromString(buf);
free(buf); /* malloc'ed */
return res;

在此示例中,对 I/O 缓冲区的内存请求由 C 库分配器处理。 Python 内存 Management 器仅涉及结果返回的字节对象的分配。

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

See also

PYTHONMALLOC环境变量可用于配置 Python 使用的内存分配器。

每次创建新的 pymalloc 对象舞台时以及关闭时, PYTHONMALLOCSTATS环境变量都可用于打印pymalloc 内存分配器的统计信息。

原始内存接口

以下Function集是系统分配器的包装器。这些函数是线程安全的,不需要保留GIL

默认的原始内存分配器使用以下Function:malloc()calloc()realloc()free();请求零字节时调用malloc(1)(或calloc(1, 1))。

3.4 版的新Function。

  • 无效* PyMem_RawMalloc(size_t * n *)
    • 分配* n *个字节,并返回void*类型的指针到分配的内存,如果请求失败,则返回NULL

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

  • void * PyMem_RawCalloc(size_t * nelem *,size_t * elsize *)
    • 分配* nelem 元素,每个元素的字节大小为 elsize *,并将类型void*的指针返回到分配的内存,如果请求失败,则返回NULL。内存被初始化为零。

请求零元素或零字节大小的元素(如果可能)将返回一个不同的非NULL指针,就好像已调用PyMem_RawCalloc(1, 1)一样。

3.5 版中的新Function。

  • void * PyMem_RawRealloc(void ** p *,size_t * n *)
    • 将* p 指向的存储块的大小调整为 n *个字节。内容将保持不变,直到新旧尺寸的最小值。

如果* p NULL,则呼叫等效于PyMem_RawMalloc(n);否则,如果 n *等于零,则将调整内存块的大小,但不会释放该内存块,并且返回的指针为非NULL

除非* p *是NULL,否则它必须是先前调用PyMem_RawMalloc()PyMem_RawRealloc()PyMem_RawCalloc()返回的。

如果请求失败,则PyMem_RawRealloc()返回NULL,* p *仍然是指向先前存储区域的有效指针。

如果* p *为NULL,则不执行任何操作。

Memory Interface

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

默认内存分配器使用pymalloc 内存分配器

Warning

使用这些Function时,必须按住GIL

在版本 3.6 中更改:默认分配器现在为 pymalloc 而不是系统malloc()

  • 无效* PyMem_Malloc(size_t * n *)
    • 分配* n *个字节,并返回void*类型的指针到分配的内存,如果请求失败,则返回NULL

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

  • void * PyMem_Calloc(size_t * nelem *,size_t * elsize *)
    • 分配* nelem 元素,每个元素的字节大小为 elsize *,并将类型void*的指针返回到分配的内存,如果请求失败,则返回NULL。内存被初始化为零。

请求零元素或零字节大小的元素(如果可能)将返回一个不同的非NULL指针,就好像已调用PyMem_Calloc(1, 1)一样。

3.5 版中的新Function。

  • void * PyMem_Realloc(void ** p *,size_t * n *)
    • 将* p 指向的存储块的大小调整为 n *个字节。内容将保持不变,直到新旧尺寸的最小值。

如果* p NULL,则呼叫等效于PyMem_Malloc(n);否则,如果 n *等于零,则将调整内存块的大小,但不会释放该内存块,并且返回的指针为非NULL

除非* p *是NULL,否则它必须是先前调用PyMem_Malloc()PyMem_Realloc()PyMem_Calloc()返回的。

如果请求失败,则PyMem_Realloc()返回NULL,* p *仍然是指向先前存储区域的有效指针。

  • 无效PyMem_Free(void ** p *)

如果* p *为NULL,则不执行任何操作。

为方便起见,提供了以下面向类型的宏。请注意,* 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 *的原始值,以避免在处理错误时丢失内存。

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

  • PyMem_MALLOC(size)

  • PyMem_NEW(type, size)

  • PyMem_REALLOC(ptr, size)

  • PyMem_RESIZE(ptr, type, size)

  • PyMem_FREE(ptr)

  • PyMem_DEL(ptr)

Object allocators

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

默认对象分配器使用pymalloc 内存分配器

Warning

使用这些Function时,必须按住GIL

  • 无效* PyObject_Malloc(size_t * n *)
    • 分配* n *个字节,并返回void*类型的指针到分配的内存,如果请求失败,则返回NULL

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

  • void * PyObject_Calloc(size_t * nelem *,size_t * elsize *)
    • 分配* nelem 元素,每个元素的字节大小为 elsize *,并将类型void*的指针返回到分配的内存,如果请求失败,则返回NULL。内存被初始化为零。

请求零元素或零字节大小的元素(如果可能)将返回一个不同的非NULL指针,就好像已调用PyObject_Calloc(1, 1)一样。

3.5 版中的新Function。

  • void * PyObject_Realloc(void ** p *,size_t * n *)
    • 将* p 指向的存储块的大小调整为 n *个字节。内容将保持不变,直到新旧尺寸的最小值。

如果* p NULL,则呼叫等效于PyObject_Malloc(n);否则,如果 n *等于零,则将调整内存块的大小,但不会释放该内存块,并且返回的指针为非NULL

除非* p *是NULL,否则它必须是先前调用PyObject_Malloc()PyObject_Realloc()PyObject_Calloc()返回的。

如果请求失败,则PyObject_Realloc()返回NULL,* p *仍然是指向先前存储区域的有效指针。

如果* p *为NULL,则不执行任何操作。

默认内存分配器

默认内存分配器:

Configuration Name PyMem_RawMalloc PyMem_Malloc PyObject_Malloc
Release build "pymalloc" malloc pymalloc pymalloc
Debug build "pymalloc_debug" malloc调试 pymalloc调试 pymalloc调试
发布构建,不带 pymalloc "malloc" malloc malloc malloc
调试构建,无需 pymalloc "malloc_debug" malloc调试 malloc调试 malloc调试

Legend:

自定义内存分配器

3.4 版的新Function。

  • PyMemAllocatorEx
    • 用于描述内存块分配器的结构。该结构包含四个字段:
Field Meaning
void *ctx 用户上下文作为第一个参数传递
void* malloc(void *ctx, size_t size) 分配一个内存块
void* calloc(void *ctx, size_t nelem, size_t elsize) 分配以零初始化的内存块
void* realloc(void *ctx, void *ptr, size_t new_size) 分配或调整内存块的大小
void free(void *ctx, void *ptr) 释放一个内存块

在版本 3.5 中进行了更改:PyMemAllocator结构已重命名为PyMemAllocatorEx,并添加了新的calloc字段。

当请求零字节时,新分配器必须返回不同的非NULL指针。

对于PYMEM_DOMAIN_RAW域,分配器必须是线程安全的:调用分配器时,不保留GIL

如果新分配器不是钩子(不调用先前的分配器),则必须调用PyMem_SetupDebugHooks()函数以将调试钩子重新安装在新分配器的顶部。

  • 无效PyMem_SetupDebugHooks(无效)
    • 安装程序钩子可检测 Python 内存分配器函数中的错误。

新分配的内存由字节0xCD(CLEANBYTE)填充,释放的内存由字节0xDD(DEADBYTE)填充。存储块被“禁止的字节”(FORBIDDENBYTE:字节0xFD)包围。

Runtime checks:

发生错误时,调试钩子使用tracemalloc模块获取分配内存块的回溯。仅当tracemalloc正在跟踪 Python 内存分配并且已跟踪内存块时,才会显示回溯。

如果 Python 是在调试模式下编译的,则这些钩子为默认安装 PYTHONMALLOC环境变量可用于在以发布模式编译的 Python 上安装调试钩子。

在版本 3.6 中更改:此Function现在也适用于以发布模式编译的 Python。出错时,调试钩子现在使用tracemalloc获取分配内存块的回溯。现在,调试钩子还会在调用PYMEM_DOMAIN_OBJPYMEM_DOMAIN_MEM域的函数时检查是否保留了 GIL。

在 3.8 版中进行了更改:字节模式0xCB(CLEANBYTE),0xDB(DEADBYTE)和0xFB(FORBIDDENBYTE)已由0xCD0xDD0xFD替换为使用与 Windows CRT 调试malloc()free()相同的值。

pymalloc 分配器

Python 具有一个* pymalloc *分配器,该分配器针对寿命较短的小型对象(小于或等于 512 字节)进行了优化。它使用称为“ arenas”的内存 Map,固定大小为 256 KiB。对于大于 512 字节的分配,它回落到PyMem_RawMalloc()PyMem_RawRealloc()

竞技场分配器使用以下Function:

  • 在 Windows 上为VirtualAlloc()VirtualFree()

  • mmap()munmap()(如果有),

  • malloc()free()否则。

自定义 pymalloc Arena 分配器

3.4 版的新Function。

  • PyObjectArenaAllocator
    • 用于描述竞技场分配器的结构。该结构包含三个字段:
Field Meaning
void *ctx 用户上下文作为第一个参数传递
void* alloc(void *ctx, size_t size) 分配大小为字节的竞技场
void free(void *ctx, size_t size, void *ptr) 自由竞技场

tracemalloc C API

3.7 版中的新Function。

  • int PyTraceMalloc_Track(unsigned int * domain *,uintptr_t * ptr *,size_t * size *)

成功返回0,错误返回-1(未能分配内存来存储跟踪)。如果禁用 tracemalloc,则返回-2

如果已经跟踪了内存块,请更新现有的跟踪。

  • int PyTraceMalloc_Untrack(unsigned int * domain *,uintptr_t * ptr *)
    • 取消跟踪tracemalloc模块中分配的内存块。如果未跟踪该块,则不执行任何操作。

如果禁用 tracemalloc,则返回-2,否则返回0

Examples

这是第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 = PyBytes_FromString(buf);
PyMem_Free(buf); /* allocated with PyMem_Malloc */
return res;

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

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 = PyBytes_FromString(buf);
PyMem_Del(buf); /* allocated with PyMem_New */
return res;

请注意,在上面的两个示例中,总是pass属于同一集合的函数来操纵缓冲区。实际上,对于给定的内存块,需要使用相同的内存 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 堆的原始内存块的Function外,还使用PyObject_New()PyObject_NewVar()PyObject_Del()分配和释放 Python 中的对象。

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