On this page
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
。
- 分配* n *个字节,并返回
如果可能,请求零字节将返回不同的非NULL
指针,就好像已调用PyMem_RawMalloc(1)
一样。内存将不会以任何方式初始化。
- void *
PyMem_RawCalloc
(size_t * nelem *,size_t * elsize *)- 分配* nelem 元素,每个元素的字节大小为 elsize *,并将类型
void*
的指针返回到分配的内存,如果请求失败,则返回NULL
。内存被初始化为零。
- 分配* nelem 元素,每个元素的字节大小为 elsize *,并将类型
请求零元素或零字节大小的元素(如果可能)将返回一个不同的非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 *仍然是指向先前存储区域的有效指针。
- 无效
PyMem_RawFree
(void ** p *)- 释放* p *指向的内存块,该内存块必须是先前调用PyMem_RawMalloc(),PyMem_RawRealloc()或PyMem_RawCalloc()返回的。否则,或者如果以前已调用
PyMem_RawFree(p)
,则会发生未定义的行为。
- 释放* p *指向的内存块,该内存块必须是先前调用PyMem_RawMalloc(),PyMem_RawRealloc()或PyMem_RawCalloc()返回的。否则,或者如果以前已调用
如果* p *为NULL
,则不执行任何操作。
Memory Interface
以下函数集是按照 ANSI C 标准建模的,但是指定了请求零字节时的行为,可用于从 Python 堆分配和释放内存。
Warning
使用这些Function时,必须按住GIL。
在版本 3.6 中更改:默认分配器现在为 pymalloc 而不是系统malloc()
。
- 无效*
PyMem_Malloc
(size_t * n *)- 分配* n *个字节,并返回
void*
类型的指针到分配的内存,如果请求失败,则返回NULL
。
- 分配* n *个字节,并返回
如果可能,请求零字节将返回不同的非NULL
指针,就好像已调用PyMem_Malloc(1)
一样。内存将不会以任何方式初始化。
- void *
PyMem_Calloc
(size_t * nelem *,size_t * elsize *)- 分配* nelem 元素,每个元素的字节大小为 elsize *,并将类型
void*
的指针返回到分配的内存,如果请求失败,则返回NULL
。内存被初始化为零。
- 分配* nelem 元素,每个元素的字节大小为 elsize *,并将类型
请求零元素或零字节大小的元素(如果可能)将返回一个不同的非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 *指向的内存块,该内存块必须是先前调用PyMem_Malloc(),PyMem_Realloc()或PyMem_Calloc()返回的。否则,或者如果以前已调用
PyMem_Free(p)
,则会发生未定义的行为。
- 释放* p *指向的内存块,该内存块必须是先前调用PyMem_Malloc(),PyMem_Realloc()或PyMem_Calloc()返回的。否则,或者如果以前已调用
如果* p *为NULL
,则不执行任何操作。
为方便起见,提供了以下面向类型的宏。请注意,* TYPE *表示任何 C 类型。
TYPE *
PyMem_New
(TYPE,size_t * n *)- 与PyMem_Malloc()相同,但分配
(n * sizeof(TYPE))
字节的内存。返回转换为TYPE*
的指针。内存将不会以任何方式初始化。
- 与PyMem_Malloc()相同,但分配
TYPE *
PyMem_Resize
(void ** p *,TYPE,size_t * n *)- 与PyMem_Realloc()相同,但是将内存块的大小调整为
(n * sizeof(TYPE))
个字节。返回转换为TYPE*
的指针。返回时,* p *将是指向新存储区的指针,如果失败则为NULL
。
- 与PyMem_Realloc()相同,但是将内存块的大小调整为
这是一个 C 预处理程序宏; * p 总是重新分配。保存 p *的原始值,以避免在处理错误时丢失内存。
- 无效
PyMem_Del
(void ** p *)- 与PyMem_Free()相同。
另外,提供了以下宏集,用于直接调用 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 堆分配和释放内存。
Warning
使用这些Function时,必须按住GIL。
- 无效*
PyObject_Malloc
(size_t * n *)- 分配* n *个字节,并返回
void*
类型的指针到分配的内存,如果请求失败,则返回NULL
。
- 分配* n *个字节,并返回
如果可能,请求零字节将返回不同的非NULL
指针,就好像已调用PyObject_Malloc(1)
一样。内存将不会以任何方式初始化。
- void *
PyObject_Calloc
(size_t * nelem *,size_t * elsize *)- 分配* nelem 元素,每个元素的字节大小为 elsize *,并将类型
void*
的指针返回到分配的内存,如果请求失败,则返回NULL
。内存被初始化为零。
- 分配* nelem 元素,每个元素的字节大小为 elsize *,并将类型
请求零元素或零字节大小的元素(如果可能)将返回一个不同的非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 *仍然是指向先前存储区域的有效指针。
- 无效
PyObject_Free
(void ** p *)- 释放* p *指向的内存块,该内存块必须是先前调用PyObject_Malloc(),PyObject_Realloc()或PyObject_Calloc()返回的。否则,或者如果以前已调用
PyObject_Free(p)
,则会发生未定义的行为。
- 释放* p *指向的内存块,该内存块必须是先前调用PyObject_Malloc(),PyObject_Realloc()或PyObject_Calloc()返回的。否则,或者如果以前已调用
如果* 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:
名称: PYTHONMALLOC环境变量的值
malloc
:标准 C 库中的系统分配器,C 函数:malloc()
,calloc()
,realloc()
和free()
pymalloc
: pymalloc 内存分配器“ debug”:PyMem_SetupDebugHooks()安装了调试钩子
自定义内存分配器
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
字段。
PyMemAllocatorDomain
- 用于标识分配器域的枚举。域:
PYMEM_DOMAIN_RAW
- Functions:
PYMEM_DOMAIN_MEM
- Functions:
PYMEM_DOMAIN_OBJ
- Functions:
无效
PyMem_GetAllocator
(PyMemAllocatorDomain 域,PyMemAllocatorEx *分配器)- 获取指定域的内存块分配器。
无效
PyMem_SetAllocator
(PyMemAllocatorDomain 域,PyMemAllocatorEx *分配器)- 设置指定域的内存块分配器。
当请求零字节时,新分配器必须返回不同的非NULL
指针。
对于PYMEM_DOMAIN_RAW域,分配器必须是线程安全的:调用分配器时,不保留GIL。
如果新分配器不是钩子(不调用先前的分配器),则必须调用PyMem_SetupDebugHooks()函数以将调试钩子重新安装在新分配器的顶部。
- 无效
PyMem_SetupDebugHooks
(无效)- 安装程序钩子可检测 Python 内存分配器函数中的错误。
新分配的内存由字节0xCD
(CLEANBYTE
)填充,释放的内存由字节0xDD
(DEADBYTE
)填充。存储块被“禁止的字节”(FORBIDDENBYTE
:字节0xFD
)包围。
Runtime checks:
检测违反 API 的行为,例如:PyObject_Free()在PyMem_Malloc()分配的缓冲区上调用
在缓冲区开始之前检测写操作(缓冲区下溢)
在缓冲区结束后检测写操作(缓冲区溢出)
调用PYMEM_DOMAIN_OBJ(例如PyObject_Malloc())和PYMEM_DOMAIN_MEM(例如PyMem_Malloc())域的分配器Function时,检查GIL是否保持
发生错误时,调试钩子使用tracemalloc模块获取分配内存块的回溯。仅当tracemalloc正在跟踪 Python 内存分配并且已跟踪内存块时,才会显示回溯。
如果 Python 是在调试模式下编译的,则这些钩子为默认安装。 PYTHONMALLOC环境变量可用于在以发布模式编译的 Python 上安装调试钩子。
在版本 3.6 中更改:此Function现在也适用于以发布模式编译的 Python。出错时,调试钩子现在使用tracemalloc获取分配内存块的回溯。现在,调试钩子还会在调用PYMEM_DOMAIN_OBJ和PYMEM_DOMAIN_MEM域的函数时检查是否保留了 GIL。
在 3.8 版中进行了更改:字节模式0xCB
(CLEANBYTE
),0xDB
(DEADBYTE
)和0xFB
(FORBIDDENBYTE
)已由0xCD
,0xDD
和0xFD
替换为使用与 Windows CRT 调试malloc()
和free()
相同的值。
pymalloc 分配器
Python 具有一个* pymalloc *分配器,该分配器针对寿命较短的小型对象(小于或等于 512 字节)进行了优化。它使用称为“ arenas”的内存 Map,固定大小为 256 KiB。对于大于 512 字节的分配,它回落到PyMem_RawMalloc()和PyMem_RawRealloc()。
- pymalloc *是PYMEM_DOMAIN_MEM(例如PyMem_Malloc())和PYMEM_DOMAIN_OBJ(例如PyObject_Malloc())域中的default allocator。
竞技场分配器使用以下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) |
自由竞技场 |
PyObject_GetArenaAllocator
(PyObjectArenaAllocator *分配器)- 获取竞技场分配器。
PyObject_SetArenaAllocator
(PyObjectArenaAllocator *分配器)- 设置竞技场分配器。
tracemalloc C API
3.7 版中的新Function。
- int
PyTraceMalloc_Track
(unsigned int * domain *,uintptr_t * ptr *,size_t * size *)- 在tracemalloc模块中跟踪分配的内存块。
成功返回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 中定义和实现新对象类型的说明中进行解释。