Memory Management

Overview

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 = PyBytes_FromString(buf);
free(buf); /* malloc'ed */
return res;

在这个例子中,I / O缓冲器的存储器请求由C库分配器处理。Python内存管理器仅涉及作为结果返回的字符串对象的分配。

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

也可以看看

PYTHONMALLOCSTATS环境变量可用于在创建新对象竞技场时和关闭时打印内存分配统计信息。

Raw Memory Interface

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

The default raw memory block allocator uses the following functions: malloc(), calloc(), realloc() and free(); call malloc(1) (or calloc(1, 1)) when requesting zero bytes.

版本3.4中的新功能。

void* PyMem_RawMalloc(size_t n)

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

如果可能,请求零字节返回一个明确的非NULL指针,如同PyMem_RawMalloc(1)被调用。内存不会以任何方式初始化。

void* PyMem_RawCalloc(size_t nelem, size_t elsize)

分配字节大小为elsizenelem元素,并将void*类型的指针返回到分配的内存,或NULL 如果请求失败。存储器初始化为零。

如果可能,请求零大小零元素或零大小的元素返回一个非NULL指针,如同PyMem_RawCalloc(1, 1) t3 >

版本3.5中的新功能。

void* PyMem_RawRealloc(void *p, size_t n)

调整p指向n字节指向的存储器块大小。内容将不变为旧和新尺寸的最小值。

如果pNULL,则该调用相当于PyMem_RawMalloc(n);否则如果n等于零,则调整存储器块的大小,但不释放,返回的指针为非NULL

Unless p is NULL, it must have been returned by a previous call to PyMem_RawMalloc(), PyMem_RawRealloc() or PyMem_RawCalloc().

如果请求失败,PyMem_RawRealloc()返回NULLp仍然是前一个内存区域的有效指针。

void PyMem_RawFree(void *p)

Frees the memory block pointed to by p, which must have been returned by a previous call to PyMem_RawMalloc(), PyMem_RawRealloc() or PyMem_RawCalloc(). 否则,或者如果PyMem_Free(p)之前被调用,则发生未定义的行为。

如果pNULL,则不执行操作。

Memory Interface

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

The default memory block allocator uses the following functions: malloc(), calloc(), realloc() and free(); call malloc(1) (or calloc(1, 1)) when requesting zero bytes.

警告

使用这些功能时,必须保持GIL

void* PyMem_Malloc(size_t n)

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

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

void* PyMem_Calloc(size_t nelem, size_t elsize)

分配字节大小为elsizenelem元素,并将void*类型的指针返回到分配的内存,或NULL 如果请求失败。存储器初始化为零。

如果可能,请求零大小零元素或大小零字节的元素返回一个明确的非NULL指针,如同PyMem_Calloc(1, 1) t3 >

版本3.5中的新功能。

void* PyMem_Realloc(void *p, size_t n)

调整p指向n字节指向的存储器块大小。内容将不变为旧和新尺寸的最小值。

如果pNULL,则调用相当于PyMem_Malloc(n);否则如果n等于零,则调整存储器块的大小,但不释放,返回的指针为非NULL

Unless p is NULL, it must have been returned by a previous call to PyMem_Malloc(), PyMem_Realloc() or PyMem_Calloc().

如果请求失败,PyMem_Realloc()返回NULLp仍然是前一个内存区域的有效指针。

void PyMem_Free(void *p)

Frees the memory block pointed to by p, which must have been returned by a previous call to PyMem_Malloc(), PyMem_Realloc() or PyMem_Calloc(). 否则,或者如果PyMem_Free(p)之前被调用,则发生未定义的行为。

如果pNULL,则不执行操作。

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

TYPE* PyMem_New(TYPE, size_t n)

PyMem_Malloc()相同,但分配(n * sizeof(TYPE)) t3 >字节的内存。返回一个转换为TYPE*的指针。内存不会以任何方式初始化。

TYPE* PyMem_Resize(void *p, TYPE, size_t n)

PyMem_Realloc()相同,但是内存块调整为(n * sizeof(TYPE) t6>字节。返回一个转换为TYPE*的指针。返回时,p将是指向新存储区的指针,或者在发生故障时NULL

这是一个C预处理器宏; p总是重新分配。保存p的原始值以避免在处理错误时丢失内存。

void 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)

Customize Memory Allocators

版本3.4中的新功能。

PyMemAllocatorEx

用于描述内存块分配器的结构。该结构有四个字段:

领域含义
void * ctx用户上下文作为第一个参数传递
void * malloc(void * ctx, size_t size) / t0>分配一个内存块
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) / t0>释放内存块

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

PyMemAllocatorDomain

用于标识分配器域的枚举。域:

void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)

获取指定域的内存块分配器。

void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)

设置指定域的内存块分配器。

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

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

如果新的分配器不是一个钩子(不调用前面的分配器),必须调用PyMem_SetupDebugHooks()函数重新安装调试钩子在新的分配器。

void PyMem_SetupDebugHooks(void)

设置挂钩以检测以下Python内存分配器函数中的错误:

新分配的内存用字节0xCB填充,释放的内存用字节0xDB填充。附加检查:

  • detect API violations, ex: PyObject_Free() called on a buffer allocated by PyMem_Malloc()
  • 在缓冲区开始之前检测写(缓冲区下溢)
  • 检测写缓冲区结束后(缓冲区溢出)

如果Python没有被编译为调试模式,该函数不执行任何操作。

Customize PyObject Arena Allocator

对于小于512字节的分配,Python有一个pymalloc分配器。这个分配器针对寿命短的小对象进行了优化。它使用固定大小为256 KB的称为“arenas”的内存映射。对于大于512字节的分配,它返回到PyMem_RawMalloc()PyMem_RawRealloc()pymallocPyObject_Malloc()使用的默认分配器。

默认的竞技场分配器使用以下功能:

  • VirtualAlloc()VirtualFree()
  • mmap()munmap()如果可用,
  • malloc()free()

版本3.4中的新功能。

PyObjectArenaAllocator

用于描述竞技场分配器的结构。该结构有三个字段:

领域含义
void * ctx用户上下文作为第一个参数传递
void * alloc(void * ctx, size_t size) / t0>分配大小字节的竞技场
void free(void * ctx, size_t size, void * ptr)免费竞技场
PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)

获取竞技场分配器。

PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)

设置竞技场分配器。

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;

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

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;

注意,在上面的两个例子中,缓冲区总是通过属于同一集合的函数来操作。实际上,对于给定的存储器块,需要使用相同的存储器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中定义和实现新的对象类型进行解释。