使對象類型支持循環垃圾回收?
Python 對循環引用的垃圾檢測與回收需要“容器”對象類型的支持,此類型的容器對象中可能包含其它容器對象。不保存其它對象的引用的類型,或者只保存原子類型(如數字或字符串)的引用的類型,不需要顯式提供垃圾回收的支持。
若要創建一個容器類,類型對象的 tp_flags 字段必須包含 Py_TPFLAGS_HAVE_GC 并提供一個 tp_traverse 處理的實現。如果該類型的實例是可變的,還需要實現 tp_clear 。
-
Py_TPFLAGS_HAVE_GC 設置了此標志位的類型的對象必須符合此處記錄的規則。為方便起見,下文把這些對象稱為容器對象。
容器類型的構造函數必須符合兩個規則:
必須使用
PyObject_GC_New()或PyObject_GC_NewVar()為這些對象分配內存。初始化了所有可能包含其他容器的引用的字段后,它必須調用
PyObject_GC_Track()。
-
TYPE*
PyObject_GC_New(TYPE, PyTypeObject?*type)? 類似于
PyObject_New(),適用于設置了Py_TPFLAGS_HAVE_GC標簽的容器對象。
-
TYPE*
PyObject_GC_NewVar(TYPE, PyTypeObject?*type, Py_ssize_t?size)? 類似于
PyObject_NewVar(),適用于設置了Py_TPFLAGS_HAVE_GC標簽的容器對象。
-
TYPE*
PyObject_GC_Resize(TYPE, PyVarObject?*op, Py_ssize_t?newsize)? 為
PyObject_NewVar()所分配對象重新調整大小。 返回調整大小后的對象或在失敗時返回NULL。 op 必須尚未被垃圾回收器追蹤。
-
void
PyObject_GC_Track(PyObject?*op)? 把對象 op 加入到垃圾回收器跟蹤的容器對象中。對象在被回收器跟蹤時必須保持有效的,因為回收器可能在任何時候開始運行。在
tp_traverse處理前的所有字段變為有效后,必須調用此函數,通常在靠近構造函數末尾的位置。
-
void
_PyObject_GC_TRACK(PyObject?*op)? PyObject_GC_Track()的宏實現版本。它不能被用于擴展模塊。3.6 版后已移除: 這個宏在 Python 3.8 中被移除。
同樣的,對象的釋放器必須符合兩個類似的規則:
在引用其它容器的字段失效前,必須調用
PyObject_GC_UnTrack()。必須使用
PyObject_GC_Del()釋放對象的內存。
-
void
PyObject_GC_Del(void?*op)? 釋放對象的內存,該對象初始化時由
PyObject_GC_New()或PyObject_GC_NewVar()分配內存。
-
void
PyObject_GC_UnTrack(void?*op)? 從回收器跟蹤的容器對象集合中移除 op 對象。 請注意可以在此對象上再次調用
PyObject_GC_Track()以將其加回到被跟蹤對象集合。 釋放器 (tp_dealloc句柄) 應當在tp_traverse句柄所使用的任何字段失效之前為對象調用此函數。
-
void
_PyObject_GC_UNTRACK(PyObject?*op)? PyObject_GC_UnTrack()的使用宏實現的版本。不能用于擴展模塊。3.6 版后已移除: 這個宏在 Python 3.8 中被移除。
tp_traverse 處理接收以下類型的函數形參。
-
int
(*visitproc)(PyObject?*object, void?*arg)? 傳給
tp_traverse處理的訪問函數的類型。object 是容器中需要被遍歷的一個對象,第三個形參對應于tp_traverse處理的 arg 。Python核心使用多個訪問者函數實現循環引用的垃圾檢測,不需要用戶自行實現訪問者函數。
tp_traverse 處理必須是以下類型:
-
int
(*traverseproc)(PyObject?*self, visitproc?visit, void?*arg)? 用于容器對象的遍歷函數。 它的實現必須對 self 所直接包含的每個對象調用 visit 函數,visit 的形參為所包含對象和傳給處理程序的 arg 值。 visit 函數調用不可附帶
NULL對象作為參數。 如果 visit 返回非零值,則該值應當被立即返回。
為了簡化 tp_traverse 處理的實現,Python提供了一個 Py_VISIT() 宏。若要使用這個宏,必須把 tp_traverse 的參數命名為 visit 和 arg 。
-
void
Py_VISIT(PyObject?*o)? 如果 o 不為
NULL,則調用 visit 回調函數,附帶參數 o 和 arg。 如果 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。
-
int
(*inquiry)(PyObject?*self)? 丟棄產生循環引用的引用。不可變對象不需要聲明此方法,因為他們不可能直接產生循環引用。需要注意的是,對象在調用此方法后必須仍是有效的(不能對引用只調用
Py_DECREF()方法)。當垃圾回收器檢測到該對象在循環引用中時,此方法會被調用。
