支援迴圈垃圾回收

Python 對檢測和回收涉及迴圈引用的垃圾的支援,需要物件型別(這些物件型別是其他物件(也可能是容器)的“容器”)的支援。不儲存對其他物件的引用,或僅儲存對原子型別(如數字或字串)的引用的型別,不需要為垃圾回收提供任何顯式支援。

要建立容器型別,型別物件的 tp_flags 欄位必須包含 Py_TPFLAGS_HAVE_GC,並提供 tp_traverse 處理程式的實現。如果該型別的例項是可變的,則還必須提供 tp_clear 實現。

Py_TPFLAGS_HAVE_GC

設定此標誌的型別物件必須符合此處記錄的規則。為方便起見,這些物件將被稱為容器物件。

容器型別的建構函式必須符合兩條規則:

  1. 物件的記憶體必須使用 PyObject_GC_NewPyObject_GC_NewVar 分配。

  2. 一旦所有可能包含對其他容器引用的欄位都已初始化,它必須呼叫 PyObject_GC_Track()

同樣,物件的解構函式也必須符合類似的規則:

  1. 在引用其他容器的欄位失效之前,必須呼叫 PyObject_GC_UnTrack()

  2. 物件的記憶體必須使用 PyObject_GC_Del() 釋放。

    警告

    如果型別添加了 Py_TPFLAGS_HAVE_GC,則它 必須 至少實現一個 tp_traverse 處理程式,或顯式使用其子類或超類中的處理程式。

    呼叫 PyType_Ready() 或間接呼叫它的某些 API(如 PyType_FromSpecWithBases()PyType_FromSpec())時,如果型別繼承自實現了垃圾回收協議的類,並且子類 包含 Py_TPFLAGS_HAVE_GC 標誌,則直譯器將自動填充 tp_flagstp_traversetp_clear 欄位。

PyObject_GC_New(TYPE, typeobj)

類似於 PyObject_New,但用於設定了 Py_TPFLAGS_HAVE_GC 標誌的容器物件。

不要直接呼叫此函式為物件分配記憶體;而是呼叫型別的 tp_alloc 槽位。

填充型別的 tp_alloc 槽位時,PyType_GenericAlloc() 優先於僅呼叫此宏的自定義函式。

此宏分配的記憶體必須使用 PyObject_GC_Del() 釋放(通常透過物件的 tp_free 槽位呼叫)。

PyObject_GC_NewVar(TYPE, typeobj, size)

類似於 PyObject_NewVar,但用於設定了 Py_TPFLAGS_HAVE_GC 標誌的容器物件。

不要直接呼叫此函式為物件分配記憶體;而是呼叫型別的 tp_alloc 槽位。

填充型別的 tp_alloc 槽位時,PyType_GenericAlloc() 優先於僅呼叫此宏的自定義函式。

此宏分配的記憶體必須使用 PyObject_GC_Del() 釋放(通常透過物件的 tp_free 槽位呼叫)。

PyObject *PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size)
這是一個不穩定 API。它可能會在次要版本中未經警告而更改。

類似於 PyObject_GC_New,但在物件末尾(偏移量 tp_basicsize 處)分配 extra_size 位元組。除了 Python 物件頭 之外,分配的記憶體初始化為零。

額外資料將與物件一同釋放,但除此之外,它不受 Python 管理。

此函式分配的記憶體必須使用 PyObject_GC_Del() 釋放(通常透過物件的 tp_free 槽位呼叫)。

警告

此函式被標記為不穩定,因為例項之後保留額外資料的最終機制尚未確定。對於分配可變數量的欄位,建議使用 PyVarObjecttp_itemsize

3.12 新版功能.

PyObject_GC_Resize(TYPE, op, newsize)

調整由 PyObject_NewVar 分配的物件的大小。返回型別為 TYPE* (指任何 C 型別) 的調整大小後的物件,失敗時返回 NULL

op 必須是 PyVarObject* 型別,並且不能被垃圾回收器跟蹤。newsize 必須是 Py_ssize_t 型別。

void PyObject_GC_Track(PyObject *op)
作為 穩定 ABI 的一部分。

將物件 op 新增到回收器跟蹤的容器物件集合中。回收器可能在意外時間執行,因此物件在被跟蹤時必須有效。這應該在 tp_traverse 處理程式跟隨的所有欄位都變得有效後呼叫,通常在建構函式的末尾附近。

int PyObject_IS_GC(PyObject *obj)

如果物件實現了垃圾回收協議,則返回非零值,否則返回 0。

如果此函式返回 0,則物件不能被垃圾回收器跟蹤。

int PyObject_GC_IsTracked(PyObject *op)
自 3.9 版本以來成為 穩定 ABI 的一部分。

如果 op 的物件型別實現了 GC 協議並且 op 當前正在被垃圾回收器跟蹤,則返回 1,否則返回 0。

這類似於 Python 函式 gc.is_tracked()

在 3.9 版本中新增。

int PyObject_GC_IsFinalized(PyObject *op)
自 3.9 版本以來成為 穩定 ABI 的一部分。

如果 op 的物件型別實現了 GC 協議並且 op 已經被垃圾回收器終結,則返回 1,否則返回 0。

這類似於 Python 函式 gc.is_finalized()

在 3.9 版本中新增。

void PyObject_GC_Del(void *op)
作為 穩定 ABI 的一部分。

釋放使用 PyObject_GC_NewPyObject_GC_NewVar 分配給物件的記憶體。

不要直接呼叫此函式來釋放物件的記憶體;而是呼叫型別的 tp_free 槽位。

不要將此函式用於由 PyObject_NewPyObject_NewVar 或相關分配函式分配的記憶體;請改用 PyObject_Free()

參見

void PyObject_GC_UnTrack(void *op)
作為 穩定 ABI 的一部分。

從回收器跟蹤的容器物件集合中移除物件 op。請注意,可以再次對此物件呼叫 PyObject_GC_Track(),將其重新新增到跟蹤物件集合中。在 tp_traverse 處理程式使用的任何欄位失效之前,解構函式(tp_dealloc 處理程式)應該為此物件呼叫此函式。

3.8 版本發生變更: _PyObject_GC_TRACK()_PyObject_GC_UNTRACK() 宏已從公共 C API 中移除。

tp_traverse 處理程式接受這種型別的函式引數:

typedef int (*visitproc)(PyObject *object, void *arg)
作為 穩定 ABI 的一部分。

傳遞給 tp_traverse 處理程式的訪問器函式的型別。函式應以要遍歷的物件作為 object,以及 tp_traverse 處理程式的第三個引數作為 arg 呼叫。visit 函式不能以 NULL 物件引數呼叫。如果 visit 返回非零值,則應立即返回該值。Python 核心使用多個訪問器函式來實現迴圈垃圾檢測;預計使用者不需要編寫自己的訪問器函式。

tp_traverse 處理程式必須具有以下型別:

typedef int (*traverseproc)(PyObject *self, visitproc visit, void *arg)
作為 穩定 ABI 的一部分。

容器物件的遍歷函式。實現必須為 self 直接包含的每個物件呼叫 visit 函式,其中 visit 的引數是被包含物件和傳遞給處理程式的 arg 值。visit 函式不得使用 NULL 物件引數呼叫。如果 visit 返回非零值,則應立即返回該值。

為了簡化編寫 tp_traverse 處理程式,提供了 Py_VISIT() 宏。要使用此宏,tp_traverse 實現必須將其引數精確命名為 visitarg

Py_VISIT(o)

如果 PyObject* o 不是 NULL,則呼叫 visit 回撥,引數為 oarg。如果 visit 返回非零值,則返回該值。使用此宏,tp_traverse 處理程式如下所示:

static int
my_traverse(Noddy *self, visitproc visit, void *arg)
{
    Py_VISIT(self->foo);
    Py_VISIT(self->bar);
    return 0;
}

tp_clear 處理程式必須是 inquiry 型別,如果物件是不可變的,則為 NULL

typedef int (*inquiry)(PyObject *self)
作為 穩定 ABI 的一部分。

釋放可能建立引用迴圈的引用。不可變物件不必定義此方法,因為它們永遠不能直接建立引用迴圈。請注意,呼叫此方法後物件仍必須有效(不要只對引用呼叫 Py_DECREF())。如果回收器檢測到此物件涉及引用迴圈,它將呼叫此方法。

控制垃圾回收器狀態

C-API 提供了以下函式來控制垃圾回收執行。

Py_ssize_t PyGC_Collect(void)
作為 穩定 ABI 的一部分。

如果垃圾回收器已啟用,則執行一次完整的垃圾回收。(請注意,gc.collect() 會無條件執行它。)

返回已收集 + 無法收集的不可達物件的數量。如果垃圾回收器已停用或正在收集,則立即返回 0。垃圾回收期間的錯誤將傳遞給 sys.unraisablehook。此函式不會引發異常。

int PyGC_Enable(void)
自 3.10 版本以來,作為 穩定 ABI 的一部分。

啟用垃圾回收器:類似於 gc.enable()。返回之前的狀態,0 表示停用,1 表示啟用。

在 3.10 版本加入。

int PyGC_Disable(void)
自 3.10 版本以來,作為 穩定 ABI 的一部分。

停用垃圾回收器:類似於 gc.disable()。返回之前的狀態,0 表示停用,1 表示啟用。

在 3.10 版本加入。

int PyGC_IsEnabled(void)
自 3.10 版本以來,作為 穩定 ABI 的一部分。

查詢垃圾回收器的狀態:類似於 gc.isenabled()。返回當前狀態,0 表示停用,1 表示啟用。

在 3.10 版本加入。

查詢垃圾回收器狀態

C-API 提供了以下介面來查詢有關垃圾回收器的資訊。

void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg)
這是一個不穩定 API。它可能會在次要版本中未經警告而更改。

對所有活動的、具備 GC 能力的物件執行提供的 callbackarg 會傳遞給 callback 的所有呼叫。

警告

如果回撥函式分配(或釋放)了新物件,則它們是否會被訪問是未定義的。

操作期間垃圾回收被停用。在回撥函式中顯式執行回收可能導致未定義的行為,例如多次訪問相同的物件或根本不訪問。

3.12 新版功能.

typedef int (*gcvisitobjects_t)(PyObject *object, void *arg)

要傳遞給 PyUnstable_GC_VisitObjects() 的訪問器函式的型別。arg 與傳遞給 PyUnstable_GC_VisitObjectsarg 相同。返回 1 以繼續迭代,返回 0 以停止迭代。其他返回值目前保留,因此返回任何其他值的行為是未定義的。

3.12 新版功能.