內(nèi)存管理?

概述?

在 Python 中,內(nèi)存管理涉及到一個包含所有 Python 對象和數(shù)據(jù)結(jié)構(gòu)的私有堆(heap)。這個私有堆的管理由內(nèi)部的 Python 內(nèi)存管理器(Python memory manager) 保證。Python 內(nèi)存管理器有不同的組件來處理各種動態(tài)存儲管理方面的問題,如共享、分割、預(yù)分配或緩存。

在最底層,一個原始內(nèi)存分配器通過與操作系統(tǒng)的內(nèi)存管理器交互,確保私有堆中有足夠的空間來存儲所有與 Python 相關(guān)的數(shù)據(jù)。在原始內(nèi)存分配器的基礎(chǔ)上,幾個對象特定的分配器在同一堆上運行,并根據(jù)每種對象類型的特點實現(xiàn)不同的內(nèi)存管理策略。例如,整數(shù)對象在堆內(nèi)的管理方式不同于字符串、元組或字典,因為整數(shù)需要不同的存儲需求和速度與空間的權(quán)衡。因此,Python 內(nèi)存管理器將一些工作分配給對象特定分配器,但確保后者在私有堆的范圍內(nèi)運行。

Python 堆內(nèi)存的管理是由解釋器來執(zhí)行,用戶對它沒有控制權(quán),即使他們經(jīng)常操作指向堆內(nèi)內(nèi)存塊的對象指針,理解這一點十分重要。Python 對象和其他內(nèi)部緩沖區(qū)的堆空間分配是由 Python 內(nèi)存管理器按需通過本文檔中列出的 Python/C API 函數(shù)進(jìn)行的。

為了避免內(nèi)存破壞,擴(kuò)展的作者永遠(yuǎn)不應(yīng)該試圖用 C 庫函數(shù)導(dǎo)出的函數(shù)來對 Python 對象進(jìn)行操作,這些函數(shù)包括: malloc(), calloc(), realloc()free()。這將導(dǎo)致 C 分配器和 Python 內(nèi)存管理器之間的混用,引發(fā)嚴(yán)重后果,這是由于它們實現(xiàn)了不同的算法,并在不同的堆上操作。但是,我們可以安全地使用 C 庫分配器為單獨的目的分配和釋放內(nèi)存塊,如下例所示:

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 緩沖區(qū)的內(nèi)存請求是由 C 庫分配器處理的。Python 內(nèi)存管理器只參與了分配作為結(jié)果返回的字節(jié)對象。

然而,在大多數(shù)情況下,建議專門從 Python 堆中分配內(nèi)存,因為后者由 Python 內(nèi)存管理器控制。例如,當(dāng)解釋器擴(kuò)展了用 C 寫的新對象類型時,就必須這樣做。使用 Python 堆的另一個原因是希望*通知* Python 內(nèi)存管理器關(guān)于擴(kuò)展模塊的內(nèi)存需求。即使所請求的內(nèi)存全部只用于內(nèi)部的、高度特定的目的,將所有的內(nèi)存請求交給 Python 內(nèi)存管理器能讓解釋器對其內(nèi)存占用的整體情況有更準(zhǔn)確的了解。因此,在某些情況下,Python 內(nèi)存管理器可能會觸發(fā)或不觸發(fā)適當(dāng)?shù)牟僮?,如垃圾回收、?nèi)存壓縮或其他預(yù)防性操作。請注意,通過使用前面例子中所示的 C 庫分配器,為 I/O 緩沖區(qū)分配的內(nèi)存會完全不受 Python 內(nèi)存管理器管理。

參見

環(huán)境變量 PYTHONMALLOC 可被用來配置 Python 所使用的內(nèi)存分配器。

環(huán)境變量 PYTHONMALLOCSTATS 可以用來在每次創(chuàng)建和關(guān)閉新的 pymalloc 對象區(qū)域時打印 pymalloc 內(nèi)存分配器 的統(tǒng)計數(shù)據(jù)。

Allocator Domains?

All allocating functions belong to one of three different "domains" (see also PyMemAllocatorDomain). These domains represent different allocation strategies and are optimized for different purposes. The specific details on how every domain allocates memory or what internal functions each domain calls is considered an implementation detail, but for debugging purposes a simplified table can be found at here. There is no hard requirement to use the memory returned by the allocation functions belonging to a given domain for only the purposes hinted by that domain (although this is the recommended practice). For example, one could use the memory returned by PyMem_RawMalloc() for allocating Python objects or the memory returned by PyObject_Malloc() for allocating memory for buffers.

The three allocation domains are:

  • Raw domain: intended for allocating memory for general-purpose memory buffers where the allocation must go to the system allocator or where the allocator can operate without the GIL. The memory is requested directly to the system.

  • "Mem" domain: intended for allocating memory for Python buffers and general-purpose memory buffers where the allocation must be performed with the GIL held. The memory is taken from the Python private heap.

  • Object domain: intended for allocating memory belonging to Python objects. The memory is taken from the Python private heap.

When freeing memory previously allocated by the allocating functions belonging to a given domain,the matching specific deallocating functions must be used. For example, PyMem_Free() must be used to free memory allocated using PyMem_Malloc().

原始內(nèi)存接口?

以下函數(shù)集封裝了系統(tǒng)分配器。這些函數(shù)是線程安全的,不需要持有 全局解釋器鎖。

default raw memory allocator 使用這些函數(shù):malloc()、 calloc()、 realloc()free();申請零字節(jié)時則調(diào)用 malloc(1) (或 calloc(1, 1)

3.4 新版功能.

void *PyMem_RawMalloc(size_t n)?

分配 n 個字節(jié)并返回一個指向分配的內(nèi)存的 void* 類型指針,如果請求失敗則返回 NULL。

請求零字節(jié)可能返回一個獨特的非 NULL 指針,就像調(diào)用了 PyMem_RawMalloc(1) 一樣。但是內(nèi)存不會以任何方式被初始化。

void *PyMem_RawCalloc(size_t nelem, size_t elsize)?

分配 nelem 個元素,每個元素的大小為 elsize 字節(jié),并返回指向分配的內(nèi)存的 void* 類型指針,如果請求失敗則返回 NULL。 內(nèi)存會被初始化為零。

請求零字節(jié)可能返回一個獨特的非 NULL 指針,就像調(diào)用了 PyMem_RawCalloc(1, 1) 一樣。

3.5 新版功能.

void *PyMem_RawRealloc(void *p, size_t n)?

p 指向的內(nèi)存塊大小調(diào)整為 n 字節(jié)。以新舊內(nèi)存塊大小中的最小值為準(zhǔn),其中內(nèi)容保持不變,

如果 pNULL ,則相當(dāng)于調(diào)用 PyMem_RawMalloc(n) ;如果 n 等于 0,則內(nèi)存塊大小會被調(diào)整,但不會被釋放,返回非 NULL 指針。

除非 pNULL ,否則它必須是之前調(diào)用 PyMem_RawMalloc() 、 PyMem_RawRealloc()PyMem_RawCalloc() 所返回的。

如果請求失敗,PyMem_RawRealloc() 返回 NULL , p 仍然是指向先前內(nèi)存區(qū)域的有效指針。

void PyMem_RawFree(void *p)?

釋放 p 指向的內(nèi)存塊。 p 必須是之前調(diào)用 PyMem_RawMalloc() 、 PyMem_RawRealloc()PyMem_RawCalloc() 所返回的指針。否則,或在 PyMem_RawFree(p) 之前已經(jīng)調(diào)用過的情況下,未定義的行為會發(fā)生。

如果 pNULL, 那么什么操作也不會進(jìn)行。

內(nèi)存接口?

以下函數(shù)集,仿照 ANSI C 標(biāo)準(zhǔn),并指定了請求零字節(jié)時的行為,可用于從Python堆分配和釋放內(nèi)存。

默認(rèn)內(nèi)存分配器 使用了 pymalloc 內(nèi)存分配器.

警告

在使用這些函數(shù)時,必須持有 全局解釋器鎖(GIL) 。

在 3.6 版更改: 現(xiàn)在默認(rèn)的分配器是 pymalloc 而非系統(tǒng)的 malloc() 。

void *PyMem_Malloc(size_t n)?
Part of the Stable ABI.

分配 n 個字節(jié)并返回一個指向分配的內(nèi)存的 void* 類型指針,如果請求失敗則返回 NULL

請求零字節(jié)可能返回一個獨特的非 NULL 指針,就像調(diào)用了 PyMem_Malloc(1) 一樣。但是內(nèi)存不會以任何方式被初始化。

void *PyMem_Calloc(size_t nelem, size_t elsize)?
Part of the Stable ABI since version 3.7.

分配 nelem 個元素,每個元素的大小為 elsize 字節(jié),并返回指向分配的內(nèi)存的 void* 類型指針,如果請求失敗則返回 NULL。 內(nèi)存會被初始化為零。

請求零字節(jié)可能返回一個獨特的非 NULL 指針,就像調(diào)用了 PyMem_Calloc(1, 1) 一樣。

3.5 新版功能.

void *PyMem_Realloc(void *p, size_t n)?
Part of the Stable ABI.

p 指向的內(nèi)存塊大小調(diào)整為 n 字節(jié)。以新舊內(nèi)存塊大小中的最小值為準(zhǔn),其中內(nèi)容保持不變,

如果 pNULL ,則相當(dāng)于調(diào)用 PyMem_Malloc(n) ;如果 n 等于 0,則內(nèi)存塊大小會被調(diào)整,但不會被釋放,返回非 NULL 指針。

除非 pNULL ,否則它必須是之前調(diào)用 PyMem_Malloc()PyMem_Realloc()PyMem_Calloc() 所返回的。

如果請求失敗,PyMem_Realloc() 返回 NULL , p 仍然是指向先前內(nèi)存區(qū)域的有效指針。

void PyMem_Free(void *p)?
Part of the Stable ABI.

釋放 p 指向的內(nèi)存塊。 p 必須是之前調(diào)用 PyMem_Malloc() 、 PyMem_Realloc()PyMem_Calloc() 所返回的指針。否則,或在 PyMem_Free(p) 之前已經(jīng)調(diào)用過的情況下,未定義的行為會發(fā)生。

如果 pNULL, 那么什么操作也不會進(jìn)行。

以下面向類型的宏為方便而提供。 注意 TYPE 可以指任何 C 類型。

TYPE *PyMem_New(TYPE, size_t n)?

PyMem_Malloc() 相同,但會分配 (n * sizeof(TYPE)) 字節(jié)的內(nèi)存。 返回一個轉(zhuǎn)換為 TYPE* 的指針。 內(nèi)存將不會以任何方式被初始化。

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

PyMem_Realloc() 相同,但內(nèi)存塊的大小被調(diào)整為 (n * sizeof(TYPE)) 字節(jié)。 返回一個轉(zhuǎn)換為 TYPE* 類型的指針。 返回時,p 將為指向新內(nèi)存區(qū)域的指針,如果失敗則返回 NULL。

這是一個 C 預(yù)處理宏, p 總是被重新賦值。請保存 p 的原始值,以避免在處理錯誤時丟失內(nèi)存。

void PyMem_Del(void *p)?

PyMem_Free() 相同

此外,我們還提供了以下宏集用于直接調(diào)用 Python 內(nèi)存分配器,而不涉及上面列出的 C API 函數(shù)。但是請注意,使用它們并不能保證跨 Python 版本的二進(jìn)制兼容性,因此在擴(kuò)展模塊被棄用。

  • PyMem_MALLOC(size)

  • PyMem_NEW(type, size)

  • PyMem_REALLOC(ptr, size)

  • PyMem_RESIZE(ptr, type, size)

  • PyMem_FREE(ptr)

  • PyMem_DEL(ptr)

對象分配器?

以下函數(shù)集,仿照 ANSI C 標(biāo)準(zhǔn),并指定了請求零字節(jié)時的行為,可用于從Python堆分配和釋放內(nèi)存。

備注

There is no guarantee that the memory returned by these allocators can be successfully cast to a Python object when intercepting the allocating functions in this domain by the methods described in the Customize Memory Allocators section.

默認(rèn)對象分配器 使用 pymalloc 內(nèi)存分配器.

警告

在使用這些函數(shù)時,必須持有 全局解釋器鎖(GIL) 。

void *PyObject_Malloc(size_t n)?
Part of the Stable ABI.

分配 n 個字節(jié)并返回一個指向分配的內(nèi)存的 void* 類型指針,如果請求失敗則返回 NULL。

請求零字節(jié)可能返回一個獨特的非 NULL 指針,就像調(diào)用了 PyObject_Malloc(1) 一樣。但是內(nèi)存不會以任何方式被初始化。

void *PyObject_Calloc(size_t nelem, size_t elsize)?
Part of the Stable ABI since version 3.7.

分配 nelem 個元素,每個元素的大小為 elsize 字節(jié),并返回指向分配的內(nèi)存的 void* 類型指針,如果請求失敗則返回 NULL。 內(nèi)存會被初始化為零。

請求零字節(jié)可能返回一個獨特的非 NULL 指針,就像調(diào)用了 PyObject_Calloc(1, 1) 一樣。

3.5 新版功能.

void *PyObject_Realloc(void *p, size_t n)?
Part of the Stable ABI.

p 指向的內(nèi)存塊大小調(diào)整為 n 字節(jié)。以新舊內(nèi)存塊大小中的最小值為準(zhǔn),其中內(nèi)容保持不變,

如果*p*是``NULL``,則相當(dāng)于調(diào)用 PyObject_Malloc(n) ;如果 n 等于 0,則內(nèi)存塊大小會被調(diào)整,但不會被釋放,返回非 NULL 指針。

除非 pNULL ,否則它必須是之前調(diào)用 PyObject_Malloc()PyObject_Realloc()PyObject_Calloc() 所返回的。

如果請求失敗,PyObject_Realloc() 返回 NULL , p 仍然是指向先前內(nèi)存區(qū)域的有效指針。

void PyObject_Free(void *p)?
Part of the Stable ABI.

釋放 p 指向的內(nèi)存塊。 p 必須是之前調(diào)用 PyObject_Malloc()PyObject_Realloc()PyObject_Calloc() 所返回的指針。否則,或在 PyObject_Free(p) 之前已經(jīng)調(diào)用過的情況下,未定義行為會發(fā)生。

如果 pNULL, 那么什么操作也不會進(jìn)行。

默認(rèn)內(nèi)存分配器?

默認(rèn)內(nèi)存分配器:

配置

名稱

PyMem_RawMalloc

PyMem_Malloc

PyObject_Malloc

發(fā)布版本

"pymalloc"

malloc

pymalloc

pymalloc

調(diào)試構(gòu)建

"pymalloc_debug"

malloc + debug

pymalloc + debug

pymalloc + debug

沒有 pymalloc 的發(fā)布版本

"malloc"

malloc

malloc

malloc

沒有 pymalloc 的調(diào)試構(gòu)建

"malloc_debug"

malloc + debug

malloc + debug

malloc + debug

說明:

自定義內(nèi)存分配器?

3.4 新版功能.

type PyMemAllocatorEx?

Structure used to describe a memory block allocator. The structure has the following fields:

含意

void *ctx

作為第一個參數(shù)傳入的用戶上下文

void* malloc(void *ctx, size_t size)

分配一個內(nèi)存塊

void* calloc(void *ctx, size_t nelem, size_t elsize)

分配一個初始化為 0 的內(nèi)存塊

void* realloc(void *ctx, void *ptr, size_t new_size)

分配一個內(nèi)存塊或調(diào)整其大小

void free(void *ctx, void *ptr)

釋放一個內(nèi)存塊

在 3.5 版更改: The PyMemAllocator structure was renamed to PyMemAllocatorEx and a new calloc field was added.

type PyMemAllocatorDomain?

用來識別分配器域的枚舉類。域有:

PYMEM_DOMAIN_RAW?

函數(shù)

PYMEM_DOMAIN_MEM?

函數(shù)

PYMEM_DOMAIN_OBJ?

函數(shù)

void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)?

獲取指定域的內(nèi)存塊分配器。

void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)?

設(shè)置指定域的內(nèi)存塊分配器。

當(dāng)請求零字節(jié)時,新的分配器必須返回一個獨特的非 NULL 指針。

對于 PYMEM_DOMAIN_RAW 域,分配器必須是線程安全的:當(dāng)分配器被調(diào)用時,不持有 全局解釋器鎖 。

如果新的分配器不是鉤子(不調(diào)用之前的分配器),必須調(diào)用 PyMem_SetupDebugHooks() 函數(shù)在新分配器上重新安裝調(diào)試鉤子。

See also PyPreConfig.allocator and Preinitialize Python with PyPreConfig.

void PyMem_SetupDebugHooks(void)?

Setup debug hooks in the Python memory allocators to detect memory errors.

Debug hooks on the Python memory allocators?

When Python is built in debug mode, the PyMem_SetupDebugHooks() function is called at the Python preinitialization to setup debug hooks on Python memory allocators to detect memory errors.

The PYTHONMALLOC environment variable can be used to install debug hooks on a Python compiled in release mode (ex: PYTHONMALLOC=debug).

The PyMem_SetupDebugHooks() function can be used to set debug hooks after calling PyMem_SetAllocator().

These debug hooks fill dynamically allocated memory blocks with special, recognizable bit patterns. Newly allocated memory is filled with the byte 0xCD (PYMEM_CLEANBYTE), freed memory is filled with the byte 0xDD (PYMEM_DEADBYTE). Memory blocks are surrounded by "forbidden bytes" filled with the byte 0xFD (PYMEM_FORBIDDENBYTE). Strings of these bytes are unlikely to be valid addresses, floats, or ASCII strings.

運行時檢查:

  • Detect API violations. For example, detect if PyObject_Free() is called on a memory block allocated by PyMem_Malloc().

  • Detect write before the start of the buffer (buffer underflow).

  • Detect write after the end of the buffer (buffer overflow).

  • Check that the GIL is held when allocator functions of PYMEM_DOMAIN_OBJ (ex: PyObject_Malloc()) and PYMEM_DOMAIN_MEM (ex: PyMem_Malloc()) domains are called.

在出錯時,調(diào)試鉤子使用 tracemalloc 模塊來回溯內(nèi)存塊被分配的位置。只有當(dāng) tracemalloc 正在追蹤 Python 內(nèi)存分配,并且內(nèi)存塊被追蹤時,才會顯示回溯。

Let S = sizeof(size_t). 2*S bytes are added at each end of each block of N bytes requested. The memory layout is like so, where p represents the address returned by a malloc-like or realloc-like function (p[i:j] means the slice of bytes from *(p+i) inclusive up to *(p+j) exclusive; note that the treatment of negative indices differs from a Python slice):

p[-2*S:-S]

Number of bytes originally asked for. This is a size_t, big-endian (easier to read in a memory dump).

p[-S]

API identifier (ASCII character):

  • 'r' for PYMEM_DOMAIN_RAW.

  • 'm' for PYMEM_DOMAIN_MEM.

  • 'o' for PYMEM_DOMAIN_OBJ.

p[-S+1:0]

Copies of PYMEM_FORBIDDENBYTE. Used to catch under- writes and reads.

p[0:N]

The requested memory, filled with copies of PYMEM_CLEANBYTE, used to catch reference to uninitialized memory. When a realloc-like function is called requesting a larger memory block, the new excess bytes are also filled with PYMEM_CLEANBYTE. When a free-like function is called, these are overwritten with PYMEM_DEADBYTE, to catch reference to freed memory. When a realloc- like function is called requesting a smaller memory block, the excess old bytes are also filled with PYMEM_DEADBYTE.

p[N:N+S]

Copies of PYMEM_FORBIDDENBYTE. Used to catch over- writes and reads.

p[N+S:N+2*S]

Only used if the PYMEM_DEBUG_SERIALNO macro is defined (not defined by default).

A serial number, incremented by 1 on each call to a malloc-like or realloc-like function. Big-endian size_t. If "bad memory" is detected later, the serial number gives an excellent way to set a breakpoint on the next run, to capture the instant at which this block was passed out. The static function bumpserialno() in obmalloc.c is the only place the serial number is incremented, and exists so you can set such a breakpoint easily.

A realloc-like or free-like function first checks that the PYMEM_FORBIDDENBYTE bytes at each end are intact. If they've been altered, diagnostic output is written to stderr, and the program is aborted via Py_FatalError(). The other main failure mode is provoking a memory error when a program reads up one of the special bit patterns and tries to use it as an address. If you get in a debugger then and look at the object, you're likely to see that it's entirely filled with PYMEM_DEADBYTE (meaning freed memory is getting used) or PYMEM_CLEANBYTE (meaning uninitialized memory is getting used).

在 3.6 版更改: The PyMem_SetupDebugHooks() function now also works on Python compiled in release mode. On error, the debug hooks now use tracemalloc to get the traceback where a memory block was allocated. The debug hooks now also check if the GIL is held when functions of PYMEM_DOMAIN_OBJ and PYMEM_DOMAIN_MEM domains are called.

在 3.8 版更改: Byte patterns 0xCB (PYMEM_CLEANBYTE), 0xDB (PYMEM_DEADBYTE) and 0xFB (PYMEM_FORBIDDENBYTE) have been replaced with 0xCD, 0xDD and 0xFD to use the same values than Windows CRT debug malloc() and free().

pymalloc 分配器?

Python 有為具有短生命周期的小對象(小于或等于 512 字節(jié))優(yōu)化的 pymalloc 分配器。它使用固定大小為 256 KiB 的稱為 "arenas" 的內(nèi)存映射。對于大于512字節(jié)的分配,它回到使用 PyMem_RawMalloc()PyMem_RawRealloc()

pymallocPYMEM_DOMAIN_MEM (例如: PyMem_Malloc()) 和 PYMEM_DOMAIN_OBJ (例如: PyObject_Malloc()) 域的 默認(rèn)分配器 。

arena 分配器使用以下函數(shù):

  • Windows 上的 VirtualAlloc()VirtualFree() ,

  • mmap()munmap() ,如果可用,

  • 否則, malloc()free() 。

This allocator is disabled if Python is configured with the --without-pymalloc option. It can also be disabled at runtime using the PYTHONMALLOC environment variable (ex: PYTHONMALLOC=malloc).

自定義 pymalloc Arena 分配器?

3.4 新版功能.

type PyObjectArenaAllocator?

用來描述一個 arena 分配器的結(jié)構(gòu)體。這個結(jié)構(gòu)體有三個字段:

含意

void *ctx

作為第一個參數(shù)傳入的用戶上下文

void* alloc(void *ctx, size_t size)

分配一塊 size 字節(jié)的區(qū)域

void free(void *ctx, void *ptr, size_t size)

釋放一塊區(qū)域

void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)?

獲取 arena 分配器

void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)?

設(shè)置 arena 分配器

tracemalloc C API?

3.7 新版功能.

int PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr, size_t size)?

tracemalloc 模塊中跟蹤一個已分配的內(nèi)存塊。

成功時返回 0,出錯時返回 -1 (無法分配內(nèi)存來保存跟蹤信息)。 如果禁用了 tracemalloc 則返回 -2。

如果內(nèi)存塊已被跟蹤,則更新現(xiàn)有跟蹤信息。

int PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)?

tracemalloc 模塊中取消跟蹤一個已分配的內(nèi)存塊。 如果內(nèi)存塊未被跟蹤則不執(zhí)行任何操作。

如果 tracemalloc 被禁用則返回 -2,否則返回 0。

例子?

以下是來自 概述 小節(jié)的示例,經(jīng)過重寫以使 I/O 緩沖區(qū)是通過使用第一個函數(shù)集從 Python 堆中分配的:

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;

使用面向類型函數(shù)集的相同代碼:

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;

請注意在以上兩個示例中,緩沖區(qū)總是通過歸屬于相同集的函數(shù)來操縱的。 事實上,對于一個給定的內(nèi)存塊必須使用相同的內(nèi)存 API 族,以便使得混合不同分配器的風(fēng)險減至最低。 以下代碼序列包含兩處錯誤,其中一個被標(biāo)記為 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 堆的原始內(nèi)存塊的函數(shù)之外, Python 中的對象是通過 PyObject_New(), PyObject_NewVar()PyObject_Del() 來分配和釋放的。

這些將在有關(guān)如何在 C 中定義和實現(xiàn)新對象類型的下一章中講解。