記憶體管理¶
概述¶
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()
分配的記憶體。
三個分配域是:
原始域:旨在為通用記憶體緩衝區分配記憶體,其中分配 必須 交給系統分配器,或者分配器可以在沒有 附加執行緒狀態 的情況下執行。記憶體直接從系統請求。參見 原始記憶體介面。
“Mem”域:旨在為 Python 緩衝區和通用記憶體緩衝區分配記憶體,其中分配必須在 附加執行緒狀態 下執行。記憶體取自 Python 私有堆。參見 記憶體介面。
物件域:旨在為 Python 物件分配記憶體。記憶體取自 Python 私有堆。參見 物件分配器。
備註
自由執行緒 構建要求僅使用“物件”域分配 Python 物件,並且所有 Python 物件都使用該域分配。這與之前的 Python 版本不同,在之前的版本中這只是一種最佳實踐,而不是一項硬性要求。
例如,緩衝區(非 Python 物件)應該使用 PyMem_Malloc()
、PyMem_RawMalloc()
或 malloc()
分配,但不能使用 PyObject_Malloc()
。
參見 記憶體分配 API。
原始記憶體介面¶
以下函式集是系統分配器的包裝器。這些函式是執行緒安全的,因此不需要 執行緒狀態 被 附加。
預設原始記憶體分配器 使用以下函式: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 堆分配和釋放記憶體。
警告
使用這些函式時必須存在 附加執行緒狀態。
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 物件。
警告
使用這些函式時必須存在 附加執行緒狀態。
-
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
,則不執行任何操作。不要直接呼叫此函式來釋放物件的記憶體;請改用型別的
tp_free
槽。不要將其用於透過
PyObject_GC_New
或PyObject_GC_NewVar
分配的記憶體;請改用PyObject_GC_Del()
。參見
PyObject_GC_Del()
是此函式的等價物,用於支援垃圾回收的型別分配的記憶體。
預設記憶體分配器¶
預設記憶體分配器
配置 |
名稱 |
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 的不同直譯器中呼叫。
如果新分配器不是鉤子(不呼叫先前的分配器),則必須呼叫
PyMem_SetupDebugHooks()
函式以重新在新分配器之上安裝除錯鉤子。另請參見
PyPreConfig.allocator
和 使用 PyPreConfig 預初始化 Python。警告
PyMem_SetAllocator()
具有以下約定:它可以在
Py_PreInitialize()
之後和Py_InitializeFromConfig()
之前呼叫,以安裝自定義記憶體分配器。除了域強加的限制(例如,原始域允許在沒有 附加執行緒狀態 的情況下呼叫分配器)之外,對已安裝的分配器沒有其他限制。有關更多資訊,請參閱 分配器域部分。如果在 Python 完成初始化後(在呼叫
Py_InitializeFromConfig()
之後)呼叫,則分配器 必須 包裝現有分配器。不支援 將當前分配器替換為其他任意分配器。
3.12 版本變更: 所有分配器都必須是執行緒安全的。
-
void PyMem_SetupDebugHooks(void)¶
設定 Python 記憶體分配器中的除錯鉤子 以檢測記憶體錯誤。
Python 記憶體分配器上的除錯鉤子¶
當 Python 以除錯模式構建時,在 Python 預初始化 期間會呼叫 PyMem_SetupDebugHooks()
函式,以在 Python 記憶體分配器上設定除錯鉤子,從而檢測記憶體錯誤。
PYTHONMALLOC
環境變數可用於在釋出模式下編譯的 Python 上安裝除錯鉤子(例如:PYTHONMALLOC=debug
)。
PyMem_SetupDebugHooks()
函式可用於在呼叫 PyMem_SetAllocator()
後設置除錯鉤子。
這些除錯鉤子會用特殊的、可識別的位模式填充動態分配的記憶體塊。新分配的記憶體填充位元組 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()
)域的分配器函式時,檢查是否存在 附加執行緒狀態。
發生錯誤時,除錯鉤子使用 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 字元)
PYMEM_DOMAIN_RAW
為'r'
。PYMEM_DOMAIN_MEM
為'm'
。PYMEM_DOMAIN_OBJ
為'o'
。
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-like 或 free-like 函式首先檢查兩端的 PYMEM_FORBIDDENBYTE 位元組是否完好無損。如果它們已被更改,診斷輸出將寫入 stderr,程式將透過 Py_FatalError() 中止。另一個主要的故障模式是當程式讀取其中一個特殊位模式並嘗試將其用作地址時引發記憶體錯誤。如果您此時進入偵錯程式並檢視物件,您很可能會發現它完全填充了 PYMEM_DEADBYTE(表示正在使用已釋放的記憶體)或 PYMEM_CLEANBYTE(表示正在使用未初始化的記憶體)。
3.6 版本變更: PyMem_SetupDebugHooks()
函式現在也適用於釋出模式下編譯的 Python。發生錯誤時,除錯鉤子現在使用 tracemalloc
獲取記憶體塊分配時的回溯。除錯鉤子現在還檢查當呼叫 PYMEM_DOMAIN_OBJ
和 PYMEM_DOMAIN_MEM
域的函式時是否存在 附加執行緒狀態。
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 分配器使用以下函式:
Windows 上的
VirtualAlloc()
和VirtualFree()
,如果可用,則為
mmap()
和munmap()
,否則為
malloc()
和free()
。
如果 Python 使用 --without-pymalloc
選項配置,則此分配器將被停用。也可以使用 PYTHONMALLOC
環境變數在執行時停用它(例如:PYTHONMALLOC=malloc
)。
通常,在使用 AddressSanitizer (--with-address-sanitizer
) 構建 Python 時停用 pymalloc 分配器是很有意義的,這有助於發現 C 程式碼中的低階錯誤。
定製 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
。
示例¶
以下是 概述 部分的示例,透過使用第一個函式集從 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_Free(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_Free() */
除了旨在處理來自 Python 堆的原始記憶體塊的函式之外,Python 中的物件使用 PyObject_New
、PyObject_NewVar
和 PyObject_Free()
進行分配和釋放。
這些將在下一章關於在 C 中定義和實現新物件型別中解釋。