記憶體管理¶
概述¶
Python 中的記憶體管理涉及一個私有堆,其中包含所有 Python 物件和資料結構。這個私有堆的管理由Python 記憶體管理器在內部確保。Python 記憶體管理器有不同的元件,處理各種動態儲存管理方面,如共享、分段、預分配或快取。
在最低級別,原始記憶體分配器透過與作業系統的記憶體管理器互動,確保私有堆中有足夠的空間來儲存所有與 Python 相關的資料。在原始記憶體分配器的基礎上,幾個特定於物件的分配器在同一個堆上操作,並實現不同的記憶體管理策略,以適應每種物件型別的特性。例如,整數物件在堆中的管理方式與字串、元組或字典不同,因為整數意味著不同的儲存需求和速度/空間權衡。因此,Python 記憶體管理器將一些工作委託給特定於物件的分配器,但確保後者在私有堆的範圍內操作。
重要的是要理解,Python 堆的管理是由直譯器本身執行的,使用者無法控制它,即使他們經常操作指向堆內記憶體塊的物件指標。Python 物件的堆空間以及其他內部緩衝區的分配由 Python 記憶體管理器透過本文件中列出的 Python/C API 函式按需執行。
為了避免記憶體損壞,擴充套件編寫者絕不應嘗試使用 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 記憶體管理器。
另請參閱
PYTHONMALLOC
環境變數可用於配置 Python 使用的記憶體分配器。
PYTHONMALLOCSTATS
環境變數可用於在每次建立新的 pymalloc 物件競技場以及關閉時列印 pymalloc 記憶體分配器的統計資訊。
分配器域¶
所有分配函式都屬於三個不同的“域”之一(另請參見 PyMemAllocatorDomain
)。這些域代表不同的分配策略,並針對不同的目的進行最佳化。關於每個域如何分配記憶體或每個域呼叫哪些內部函式的具體細節被認為是實現細節,但出於除錯目的,可以在此處找到一個簡化的表格。用於分配和釋放記憶體塊的 API 必須來自同一域。例如,PyMem_Free()
必須用於釋放使用 PyMem_Malloc()
分配的記憶體。
三個分配域是
原始域:用於為通用記憶體緩衝區分配記憶體,其中分配必須轉到系統分配器,或者分配器可以在沒有 GIL 的情況下操作。記憶體直接從系統請求。請參閱 原始記憶體介面。
“Mem”域:用於為 Python 緩衝區和通用記憶體緩衝區分配記憶體,其中必須在持有 GIL 的情況下執行分配。記憶體取自 Python 私有堆。請參閱 記憶體介面。
物件域:用於為 Python 物件分配記憶體。記憶體取自 Python 私有堆。請參閱 物件分配器。
注意
自由執行緒 構建要求僅使用“物件”域分配 Python 物件,並且所有 Python 物件都使用該域分配。這與以前的 Python 版本不同,在以前的 Python 版本中,這只是一種最佳實踐,而不是硬性要求。
例如,應使用 PyMem_Malloc()
、PyMem_RawMalloc()
或 malloc()
分配緩衝區(非 Python 物件),但不應使用 PyObject_Malloc()
分配。
請參閱 記憶體分配 API。
原始記憶體介面¶
以下函式集是系統分配器的包裝器。這些函式是執行緒安全的,不需要持有 GIL。
預設原始記憶體分配器使用以下函式:malloc()
、calloc()
、realloc()
和 free()
;請求零位元組時呼叫 malloc(1)
(或 calloc(1, 1)
)。
在 3.4 版本中新增。
-
void *PyMem_RawMalloc(size_t n)¶
- 自 3.13 版本以來,是 穩定 ABI 的一部分。
分配 n 個位元組,並返回指向已分配記憶體的 void* 型別的指標,如果請求失敗,則返回
NULL
。如果可能,請求零位元組將返回一個不同的非
NULL
指標,就像改為呼叫了PyMem_RawMalloc(1)
一樣。記憶體將不會以任何方式初始化。
-
void *PyMem_RawCalloc(size_t nelem, size_t elsize)¶
- 自 3.13 版本以來,是 穩定 ABI 的一部分。
分配 nelem 個元素,每個元素的大小為 elsize 位元組,並返回一個型別為 void* 的指標,指向已分配的記憶體。如果請求失敗,則返回
NULL
。該記憶體會被初始化為零。如果請求零個元素或大小為零位元組的元素,如果可能,將返回一個不同的非
NULL
指標,就像呼叫了PyMem_RawCalloc(1, 1)
一樣。在 3.5 版本中新增。
-
void *PyMem_RawRealloc(void *p, size_t n)¶
- 自 3.13 版本以來,是 穩定 ABI 的一部分。
將 p 指向的記憶體塊調整大小為 n 位元組。內容將保持不變,直至舊大小和新大小的最小值。
如果 p 為
NULL
,則該呼叫等同於PyMem_RawMalloc(n)
;否則,如果 n 等於零,則記憶體塊將調整大小,但不會被釋放,並且返回的指標為非NULL
。除非 p 為
NULL
,否則它必須是由先前呼叫PyMem_RawMalloc()
、PyMem_RawRealloc()
或PyMem_RawCalloc()
返回的。如果請求失敗,
PyMem_RawRealloc()
將返回NULL
,並且 p 仍然是指向前一個記憶體區域的有效指標。
-
void PyMem_RawFree(void *p)¶
- 自 3.13 版本以來,是 穩定 ABI 的一部分。
釋放 p 指向的記憶體塊,該記憶體塊必須是由先前呼叫
PyMem_RawMalloc()
、PyMem_RawRealloc()
或PyMem_RawCalloc()
返回的。否則,或者如果之前已呼叫過PyMem_RawFree(p)
,則會發生未定義的行為。如果 p 為
NULL
,則不執行任何操作。
記憶體介面¶
以下函式集仿照 ANSI C 標準,但指定了請求零位元組時的行為,可用於從 Python 堆分配和釋放記憶體。
警告
使用這些函式時,必須持有 GIL。
在 3.6 版本中更改: 預設分配器現在是 pymalloc 而不是系統 malloc()
。
-
void *PyMem_Malloc(size_t n)¶
- 屬於 穩定 ABI 的一部分。
分配 n 個位元組,並返回指向已分配記憶體的 void* 型別的指標,如果請求失敗,則返回
NULL
。如果可能,請求零位元組將返回一個不同的非
NULL
指標,就像呼叫了PyMem_Malloc(1)
一樣。該記憶體將不會以任何方式初始化。
-
void *PyMem_Calloc(size_t nelem, size_t elsize)¶
- 自 3.7 版本以來,屬於 穩定 ABI 的一部分。
分配 nelem 個元素,每個元素的大小為 elsize 位元組,並返回一個型別為 void* 的指標,指向已分配的記憶體。如果請求失敗,則返回
NULL
。該記憶體會被初始化為零。如果可能,請求零個元素或大小為零位元組的元素將返回一個不同的非
NULL
指標,就像呼叫了PyMem_Calloc(1, 1)
一樣。在 3.5 版本中新增。
-
void *PyMem_Realloc(void *p, size_t n)¶
- 屬於 穩定 ABI 的一部分。
將 p 指向的記憶體塊調整大小為 n 位元組。內容將保持不變,直至舊大小和新大小的最小值。
如果 p 為
NULL
,則該呼叫等同於PyMem_Malloc(n)
;否則,如果 n 等於零,則記憶體塊將調整大小,但不會被釋放,並且返回的指標為非NULL
。除非 p 為
NULL
,否則它必須是由先前呼叫PyMem_Malloc()
、PyMem_Realloc()
或PyMem_Calloc()
返回的。如果請求失敗,
PyMem_Realloc()
將返回NULL
,並且 p 仍然是指向前一個記憶體區域的有效指標。
-
void PyMem_Free(void *p)¶
- 屬於 穩定 ABI 的一部分。
釋放 p 指向的記憶體塊,該記憶體塊必須是由先前呼叫
PyMem_Malloc()
、PyMem_Realloc()
或PyMem_Calloc()
返回的。否則,或者如果之前已呼叫過PyMem_Free(p)
,則會發生未定義的行為。如果 p 為
NULL
,則不執行任何操作。
為了方便起見,提供了以下面向型別的宏。請注意,TYPE 指的是任何 C 型別。
-
PyMem_New(TYPE, n)¶
與
PyMem_Malloc()
相同,但分配(n * sizeof(TYPE))
位元組的記憶體。返回一個強制轉換為TYPE*
的指標。該記憶體將不會以任何方式初始化。
-
PyMem_Resize(p, TYPE, 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(size)
PyMem_NEW(type, size)
PyMem_REALLOC(ptr, size)
PyMem_RESIZE(ptr, type, size)
PyMem_FREE(ptr)
PyMem_DEL(ptr)
物件分配器¶
以下函式集仿照 ANSI C 標準,但指定了請求零位元組時的行為,可用於從 Python 堆分配和釋放記憶體。
注意
不能保證當透過 自定義記憶體分配器 部分中描述的方法攔截此域中的分配函式時,這些分配器返回的記憶體可以成功轉換為 Python 物件。
警告
使用這些函式時,必須持有 GIL。
-
void *PyObject_Malloc(size_t n)¶
- 屬於 穩定 ABI 的一部分。
分配 n 個位元組,並返回指向已分配記憶體的 void* 型別的指標,如果請求失敗,則返回
NULL
。如果可能,請求零位元組會返回一個不同的非
NULL
指標,就像呼叫了PyObject_Malloc(1)
一樣。記憶體將不會以任何方式初始化。
-
void *PyObject_Calloc(size_t nelem, size_t elsize)¶
- 自 3.7 版本以來,屬於 穩定 ABI 的一部分。
分配 nelem 個元素,每個元素的大小為 elsize 位元組,並返回一個型別為 void* 的指標,指向已分配的記憶體。如果請求失敗,則返回
NULL
。該記憶體會被初始化為零。如果可能,請求零個元素或零位元組大小的元素會返回一個不同的非
NULL
指標,就像呼叫了PyObject_Calloc(1, 1)
一樣。在 3.5 版本中新增。
-
void *PyObject_Realloc(void *p, size_t n)¶
- 屬於 穩定 ABI 的一部分。
將 p 指向的記憶體塊調整大小為 n 位元組。內容將保持不變,直至舊大小和新大小的最小值。
如果 p 為
NULL
,則呼叫等效於PyObject_Malloc(n)
;否則,如果 n 等於零,則記憶體塊會被調整大小但不會釋放,並且返回的指標是非NULL
的。除非 p 為
NULL
,否則它必須是由先前呼叫PyObject_Malloc()
、PyObject_Realloc()
或PyObject_Calloc()
返回的。如果請求失敗,
PyObject_Realloc()
返回NULL
並且 p 仍然是指向前一個記憶體區域的有效指標。
-
void PyObject_Free(void *p)¶
- 屬於 穩定 ABI 的一部分。
釋放 p 指向的記憶體塊,該記憶體塊必須是由先前呼叫
PyObject_Malloc()
、PyObject_Realloc()
或PyObject_Calloc()
返回的。否則,或者如果之前已呼叫PyObject_Free(p)
,則會發生未定義的行為。如果 p 為
NULL
,則不執行任何操作。
預設記憶體分配器¶
預設記憶體分配器
配置 |
名稱 |
PyMem_RawMalloc |
PyMem_Malloc |
PyObject_Malloc |
---|---|---|---|---|
釋出版本 |
|
|
|
|
除錯版本 |
|
|
|
|
釋出版本,無 pymalloc |
|
|
|
|
除錯版本,無 pymalloc |
|
|
|
|
圖例
名稱:
PYTHONMALLOC
環境變數的值。malloc
:標準 C 庫中的系統分配器,C 函式:malloc()
、calloc()
、realloc()
和free()
。pymalloc
:pymalloc 記憶體分配器。mimalloc
:mimalloc 記憶體分配器。如果 mimalloc 支援不可用,則將使用 pymalloc 分配器。“+ 除錯”:使用 Python 記憶體分配器上的除錯鉤子。
“除錯版本”:以除錯模式構建的 Python。
自定義記憶體分配器¶
在 3.4 版本中新增。
-
type PyMemAllocatorEx¶
用於描述記憶體塊分配器的結構。該結構具有以下欄位
欄位
含義
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
欄位。
-
type PyMemAllocatorDomain¶
用於標識分配器域的列舉。域
-
PYMEM_DOMAIN_RAW¶
函式
-
PYMEM_DOMAIN_MEM¶
函式
-
PYMEM_DOMAIN_OBJ¶
函式
-
PYMEM_DOMAIN_RAW¶
-
void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)¶
獲取指定域的記憶體塊分配器。
-
void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)¶
設定指定域的記憶體塊分配器。
新的分配器在請求零位元組時必須返回一個不同的非
NULL
指標。對於
PYMEM_DOMAIN_RAW
域,分配器必須是執行緒安全的:當呼叫分配器時,全域性直譯器鎖(GIL) 不會被持有。對於其餘域,分配器也必須是執行緒安全的:分配器可能會在不共享
GIL
的不同直譯器中被呼叫。如果新的分配器不是鉤子(不呼叫之前的分配器),則必須呼叫
PyMem_SetupDebugHooks()
函式,以在新分配器的基礎上重新安裝除錯鉤子。另請參閱
PyPreConfig.allocator
和 使用 PyPreConfig 預初始化 Python。警告
PyMem_SetAllocator()
具有以下約定它可以在
Py_PreInitialize()
之後和Py_InitializeFromConfig()
之前呼叫,以安裝自定義記憶體分配器。除了域施加的限制(例如,Raw 域允許在不持有 GIL 的情況下呼叫分配器)之外,對安裝的分配器沒有任何限制。有關更多資訊,請參見 關於分配器域的部分。如果在 Python 完成初始化之後(在呼叫
Py_InitializeFromConfig()
之後)呼叫,則分配器 **必須** 包裝現有的分配器。 **不支援** 將當前分配器替換為其他任意分配器。
在 3.12 版本中更改: 所有分配器必須是執行緒安全的。
-
void PyMem_SetupDebugHooks(void)¶
在 Python 記憶體分配器中設定除錯鉤子 以檢測記憶體錯誤。
Python 記憶體分配器上的除錯鉤子¶
當 Python 以除錯模式構建時,PyMem_SetupDebugHooks()
函式會在 Python 預初始化 時呼叫,以便在 Python 記憶體分配器上設定除錯鉤子,以檢測記憶體錯誤。
可以使用 PYTHONMALLOC
環境變數在以釋出模式編譯的 Python 上安裝除錯鉤子(例如:PYTHONMALLOC=debug
)。
可以在呼叫 PyMem_SetAllocator()
之後使用 PyMem_SetupDebugHooks()
函式來設定除錯鉤子。
這些除錯鉤子會用特殊的、可識別的位模式填充動態分配的記憶體塊。新分配的記憶體填充位元組 0xCD
(PYMEM_CLEANBYTE
),釋放的記憶體填充位元組 0xDD
(PYMEM_DEADBYTE
)。記憶體塊周圍環繞著用位元組 0xFD
(PYMEM_FORBIDDENBYTE
) 填充的 “禁止位元組”。這些位元組的字串不太可能是有效的地址、浮點數或 ASCII 字串。
執行時檢查
檢測 API 違規。例如,檢測是否在
PyMem_Malloc()
分配的記憶體塊上呼叫了PyObject_Free()
。檢測在緩衝區開始之前的寫入(緩衝區下溢)。
檢測在緩衝區結束之後的寫入(緩衝區溢位)。
檢查當呼叫
PYMEM_DOMAIN_OBJ
(例如:PyObject_Malloc()
) 和PYMEM_DOMAIN_MEM
(例如:PyMem_Malloc()
) 域的分配器函式時,是否持有 GIL。
發生錯誤時,除錯鉤子會使用 tracemalloc
模組來獲取分配記憶體塊的回溯。只有在 tracemalloc
正在跟蹤 Python 記憶體分配,並且記憶體塊被跟蹤時才會顯示回溯。
設 S = sizeof(size_t)
。在請求的每個 N 位元組塊的每一端都會新增 2*S
位元組。記憶體佈局如下所示,其中 p 表示 malloc 或 realloc 類函式返回的地址(p[i:j]
表示從 *(p+i)
(包含) 到 *(p+j)
(不包含) 的位元組切片;請注意,負索引的處理方式與 Python 切片不同)
p[-2*S:-S]
最初請求的位元組數。這是一個 size_t,大端位元組序(在記憶體轉儲中更容易讀取)。
p[-S]
API 識別符號(ASCII 字元)
'r'
用於PYMEM_DOMAIN_RAW
。'm'
用於PYMEM_DOMAIN_MEM
。'o'
用於PYMEM_DOMAIN_OBJ
。
p[-S+1:0]
PYMEM_FORBIDDENBYTE 的副本。用於捕獲下寫和下讀。
p[0:N]
請求的記憶體,填充 PYMEM_CLEANBYTE 的副本,用於捕獲對未初始化記憶體的引用。當呼叫 realloc 類函式請求更大的記憶體塊時,新的多餘位元組也會填充 PYMEM_CLEANBYTE。當呼叫 free 類函式時,這些位元組會被 PYMEM_DEADBYTE 覆蓋,以捕獲對已釋放記憶體的引用。當呼叫 realloc 類函式請求較小的記憶體塊時,多餘的舊位元組也會填充 PYMEM_DEADBYTE。
p[N:N+S]
PYMEM_FORBIDDENBYTE 的副本。用於捕獲溢寫和溢讀。
p[N+S:N+2*S]
僅當定義了
PYMEM_DEBUG_SERIALNO
宏時才使用(預設情況下未定義)。一個序列號,每次呼叫 malloc 或 realloc 類函式時加 1。大端位元組序
size_t
。如果稍後檢測到“壞記憶體”,則序列號提供了一個極好的方法,可以在下次執行時設定斷點,以捕獲此塊被傳遞出去的瞬間。obmalloc.c 中的靜態函式 bumpserialno() 是唯一會遞增序列號的地方,它的存在是為了方便您輕鬆設定此類斷點。
realloc 或 free 類函式首先檢查每一端的 PYMEM_FORBIDDENBYTE 位元組是否完好無損。如果它們被更改,則會將診斷輸出寫入 stderr,並透過 Py_FatalError() 中止程式。另一個主要的故障模式是當程式讀取其中一種特殊的位模式並嘗試將其用作地址時,會引發記憶體錯誤。如果進入偵錯程式並檢視物件,您可能會看到它完全填充了 PYMEM_DEADBYTE(表示正在使用已釋放的記憶體)或 PYMEM_CLEANBYTE(表示正在使用未初始化的記憶體)。
在 3.6 版本中更改: PyMem_SetupDebugHooks()
函式現在也適用於以釋出模式編譯的 Python。發生錯誤時,除錯鉤子現在使用 tracemalloc
來獲取分配記憶體塊的回溯。當呼叫 PYMEM_DOMAIN_OBJ
和 PYMEM_DOMAIN_MEM
域的函式時,除錯鉤子現在還會檢查是否持有 GIL。
3.8 版本更改: 位元組模式 0xCB
(PYMEM_CLEANBYTE
), 0xDB
(PYMEM_DEADBYTE
) 和 0xFB
(PYMEM_FORBIDDENBYTE
) 已被替換為 0xCD
, 0xDD
和 0xFD
,以使用與 Windows CRT 除錯 malloc()
和 free()
相同的值。
pymalloc 分配器¶
Python 有一個 pymalloc 分配器,針對具有短生命週期的小物件(小於或等於 512 位元組)進行了最佳化。它使用稱為“arena”的記憶體對映,在 32 位平臺上固定大小為 256 KiB,在 64 位平臺上固定大小為 1 MiB。對於大於 512 位元組的分配,它會回退到 PyMem_RawMalloc()
和 PyMem_RawRealloc()
。
pymalloc 是 預設分配器,適用於 PYMEM_DOMAIN_MEM
(例如:PyMem_Malloc()
) 和 PYMEM_DOMAIN_OBJ
(例如:PyObject_Malloc()
) 域。
arena 分配器使用以下函式
VirtualAlloc()
和VirtualFree()
(在 Windows 上),mmap()
和munmap()
(如果可用),malloc()
和free()
(否則)。
如果 Python 配置了 --without-pymalloc
選項,則停用此分配器。它也可以在執行時使用 PYTHONMALLOC
環境變數停用 (例如:PYTHONMALLOC=malloc
)。
自定義 pymalloc Arena 分配器¶
在 3.4 版本中新增。
-
type PyObjectArenaAllocator¶
用於描述 arena 分配器的結構。該結構有三個欄位
欄位
含義
void *ctx
作為第一個引數傳遞的使用者上下文
void* alloc(void *ctx, size_t size)
分配一個大小為 size 位元組的 arena
void free(void *ctx, void *ptr, size_t size)
釋放一個 arena
-
void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)¶
獲取 arena 分配器。
-
void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)¶
設定 arena 分配器。
mimalloc 分配器¶
3.13 版本新增。
當底層平臺支援可用時,Python 支援 mimalloc 分配器。mimalloc “是一個具有出色效能特徵的通用分配器。最初由 Daan Leijen 為 Koka 和 Lean 語言的執行時系統開發。”
tracemalloc C API¶
3.7 版本新增。
-
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
。
示例¶
這是 概述 部分中的示例,經過重寫,以便 I/O 緩衝區透過使用第一個函式集從 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;
使用面向型別的函式集的相同程式碼
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 系列,以便將混合使用不同分配器的風險降至最低。以下程式碼序列包含兩個錯誤,其中一個被標記為致命錯誤,因為它混合了在不同堆上執行的兩個不同的分配器。
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 中定義和實現新物件型別的內容中進行解釋。