循環参照ガベージコレクションをサポートする¶
Python が循環参照を含むガベージの検出とコレクションをサポートするには、他のオブジェクトに対する "コンテナ" (他のオブジェクトには他のコンテナも含みます) となるオブジェクト型によるサポートが必要です。他のオブジェクトに対する参照を記憶しないオブジェクトや、(数値や文字列のような) アトム型 (atomic type) への参照だけを記憶するような型では、ガベージコレクションに際して特別これといったサポートを提供する必要はありません。
コンテナ型を作成するには、型オブジェクトの tp_flags フィールドに Py_TPFLAGS_HAVE_GC を含め、tp_traverse ハンドラを実装する必要があります。
型のインスタンスが変更可能な場合は、tp_clear も実装する必要があります。
Py_TPFLAGS_HAVE_GCこのフラグをセットした型のオブジェクトは、この節に述べた規則に適合しなければなりません。簡単のため、このフラグをセットした型のオブジェクトをコンテナオブジェクトと呼びます。
コンテナ型のコンストラクタは以下の二つの規則に適合しなければなりません:
オブジェクトのメモリは
PyObject_GC_NewまたはPyObject_GC_NewVarで割り当てなければなりません。他のコンテナへの参照が入るかもしれないフィールドが全て初期化されたら、すぐに
PyObject_GC_Track()を呼び出さなければなりません。
同様に、オブジェクトのメモリ解放関数も以下の二つの規則に適合しなければなりません:
他のコンテナを参照しているフィールドを無効化する前に、
PyObject_GC_UnTrack()を呼び出さなければなりません。オブジェクトのメモリは
PyObject_GC_Del()で解放しなければなりません。警告
If a type adds the Py_TPFLAGS_HAVE_GC, then it must implement at least a
tp_traversehandler or explicitly use one from its subclass or subclasses.PyType_Ready()、またはPyType_FromSpecWithBases()やPyType_FromSpec()など間接的にこの関数を呼び出す API を使った際、 ガベージコレクタのプロトコルを実装するクラスを継承しており、派生クラスにPy_TPFLAGS_HAVE_GCフラグが含まれて いない 場合は、 インタープリタがtp_flags、tp_traverse、tp_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)¶
- これは Unstable APIです。マイナーリリースで予告なく変更されることがあります。
PyObject_GC_Newと同様ですが、オブジェクトの末尾 (オフセットtp_basicsize) に extra_size バイトを割り当てます。 割り当てたメモリはPython オブジェクトのヘッダーを除いてゼロで初期化されます。追加データはオブジェクトとともに解放されますが、それ以外の管理は Python では行いません。
この関数で割り当てたメモリは
PyObject_GC_Del()で解放しなければなりません (通常はオブジェクトのtp_freeスロットを介して呼び出します)。警告
インスタンスの後ろに追加データを確保する最終的な仕組みが未定のため、この関数は不安定とされています。 可変個数のフィールドを割り当てるには、代わりに
PyVarObjectとtp_itemsizeを使うことが推奨されます。Added in version 3.12.
-
PyObject_GC_Resize(TYPE, op, newsize)¶
Resize an object allocated by
PyObject_NewVar. Returns the resized object of typeTYPE*(refers to any C type) orNULLon failure.op の型は PyVarObject* でなければならず、まだコレクタの追跡対象になっていてはいけません。 newsize の型は
Py_ssize_tでなければなりません。
-
void PyObject_GC_Track(PyObject *op)¶
- 次に属します: Stable ABI.
オブジェクト op を、コレクタが追跡するコンテナオブジェクトの集合に追加します。 コレクタは予期しないタイミングで実行される可能性があるため、追跡中のオブジェクトは有効な状態でなければなりません。
tp_traverseハンドラがたどるすべてのフィールドが有効になった時点で、通常はコンストラクタの終わり近くで呼び出してください。
-
int PyObject_IS_GC(PyObject *obj)¶
オブジェクトがガベージコレクタのプロトコルを実装している場合はゼロ以外、そうでなければ 0 を返します。
この関数が 0 を返す場合、そのオブジェクトをガベージコレクタの追跡対象にはできません。
-
int PyObject_GC_IsTracked(PyObject *op)¶
- 次に属します: Stable ABI (バージョン 3.9 より).
op の型が GC プロトコルを実装しており、op が現在ガベージコレクタに追跡されている場合は 1 を、それ以外の場合は 0 を返します。
Python の
gc.is_tracked()関数に相当します。Added in version 3.9.
-
int PyObject_GC_IsFinalized(PyObject *op)¶
- 次に属します: Stable ABI (バージョン 3.9 より).
op の型が GC プロトコルを実装しており、op がガベージコレクタによってすでにファイナライズされている場合は 1 を、それ以外の場合は 0 を返します。
Python の
gc.is_finalized()関数に相当します。Added in version 3.9.
-
void PyObject_GC_Del(void *op)¶
- 次に属します: Stable ABI.
PyObject_GC_NewまたはPyObject_GC_NewVarでオブジェクトに割り当てたメモリを解放します。オブジェクトのメモリを解放するためにこれを直接呼び出してはいけません。 代わりに型の
tp_freeスロットを呼び出してください。PyObject_New、PyObject_NewVar、または関連する割り当て関数で割り当てたメモリには使わないでください。 代わりにPyObject_Free()を使ってください。参考
PyObject_Free()はこの関数の非 GC 版です。
-
void PyObject_GC_UnTrack(void *op)¶
- 次に属します: Stable 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)¶
- 次に属します: Stable ABI.
tp_traverseハンドラに渡されるビジター関数 (visitor function) の型です。この関数は、探索するオブジェクトを object として、tp_traverseハンドラの第 3 引数を arg として呼び出します。 Python のコアはいくつかのビジター関数を使って、ゴミとなった循環参照を検出する仕組みを実装します; ユーザが自身のためにビジター関数を書く必要が出てくることはないでしょう。
tp_clear ハンドラは inquiry 型であるか、オブジェクトが不変 (immutable) な場合は NULL でなければなりません。
-
typedef int (*inquiry)(PyObject *self)¶
- 次に属します: Stable ABI.
循環参照を形成しているとおぼしき参照群を放棄します。変更不可能なオブジェクトは循環参照を直接形成することが決してないので、この関数を定義する必要はありません。このメソッドを呼び出した後でもオブジェクトは有効なままでなければならないので注意してください (参照に対して
Py_DECREF()を呼ぶだけにしないでください)。ガベージコレクタは、オブジェクトが循環参照を形成していることを検出した際にこのメソッドを呼び出します。
巡回¶
tp_traverse ハンドラは次の型を持っていなければなりません:
-
typedef int (*traverseproc)(PyObject *self, visitproc visit, void *arg)¶
- 次に属します: Stable ABI.
ガベージコレクタが循環参照を検出するために使う、ガベージコレクション対象オブジェクトの巡回関数です。 実装では、self が直接保持するオブジェクトごとに visit 関数を呼び出し、そのオブジェクトとハンドラに渡された arg を引数として渡さなければなりません。 オブジェクト引数を
NULLにして visit 関数を呼び出してはいけません。 visit がゼロ以外の値を返した場合は、その値を直ちに返す必要があります。典型的な
tp_traverse関数は、インスタンスが所有する Python オブジェクトのメンバーそれぞれに対して、便利なマクロPy_VISIT()を呼び出します。 例えば、次はthreading.localクラスの巡回関数です (やや古い実装です)。static int local_traverse(PyObject *op, visitproc visit, void *arg) { localobject *self = (localobject *) op; Py_VISIT(Py_TYPE(self)); Py_VISIT(self->args); Py_VISIT(self->kw); Py_VISIT(self->dict); return 0; }
注釈
Py_VISIT()を使うには、local_traverse()の visit 引数と arg 引数を正確にこの名前にする必要があります。 任意の名前を付けてはいけません。ヒープに割り当てられた型 のインスタンスは、自分の型への参照を保持します。 したがって、その巡回関数では型も巡回する必要があります。
Py_VISIT(Py_TYPE(self));
代わりに、ヒープに割り当てられた基底クラス (または適切であれば他のヒープに割り当てられた型) の
tp_traverseを呼び出して、この処理を委譲することもできます。 どちらも行わない場合、型オブジェクトがガベージコレクションで回収されない可能性があります。tp_flagsフィールドにPy_TPFLAGS_MANAGED_DICTビットが設定されている場合、巡回関数は次のようにPyObject_VisitManagedDict()を呼び出す必要があります。int err = PyObject_VisitManagedDict((PyObject*)self, visit, arg); if (err) { return err; }
Only the members that the instance owns (by having strong references to them) must be visited. For instance, if an object supports weak references via the
tp_weaklistslot, the pointer supporting the linked list (what tp_weaklist points to) must not be visited as the instance does not directly own the weak references to itself.巡回関数には次の制約があります。
警告
巡回関数は副作用を起こしてはいけません。 実装では、直接的にも間接的にも、Python オブジェクトの参照カウントを変更したり、Python オブジェクトを作成または破棄したりしてはいけません。
つまり、Python C API の ほとんど の関数は使用できません。 新しい例外を送出したり、結果のオブジェクトへの新しい参照を返したり、内部処理で副作用を起こしたりするためです。 また、特に明記されていない限り、たまたま副作用がない関数でも、将来のバージョンで予告なく副作用を起こすようになる可能性があります。
安全に使える関数の一覧は、後述の 専用の節 を参照してください。
注釈
循環参照に関与しないことを証明できるメンバーについては、
Py_VISIT()の呼び出しを省略できます。 上のlocal_traverseの例にはself->keyメンバーもありますが、この値はNULLまたは Python の文字列に限られるので、循環参照の一部にはなりません。一方、メンバーが循環参照の一部にならないと分かっていても、デバッグのために巡回しておくと、
gcモジュールのget_referents()関数の結果にそのメンバーを含められます。注釈
tp_traverse関数は、どのスレッドからも呼び出される可能性があります。バージョン 3.9 で変更: ヒープに割り当てられた型は、
tp_traverseでPy_TYPE(self)を巡回することが期待されます。 以前のバージョンの Python では、バグ 40217 によって、この処理がサブクラスのクラッシュを引き起こす場合があります。
tp_traverse ハンドラを簡単に記述できるように、Py_VISIT() マクロが用意されています。
このマクロを使うには、tp_traverse の実装で引数の名前を正確に visit と arg にする必要があります。
-
Py_VISIT(o)¶
PyObject* 型の o が
NULLでなければ、o と arg を引数として visit コールバックを呼び出します。 visit がゼロ以外の値を返した場合は、その値を返します。おおむね次のコードに相当します。
#define Py_VISIT(o) \ if (op) { \ int visit_result = visit(o, arg); \ if (visit_result != 0) { \ return visit_result; \ } \ }
巡回中に安全に使える関数¶
次の関数とマクロは tp_traverse ハンドラ内で安全に使えます。
tp_traverseに渡された visit 関数Py_TYPE():tp_traverseハンドラから呼び出した場合、Py_TYPE()の結果はそのハンドラの呼び出し中ずっと有効ですPyObject_TypeCheck(),PyType_IsSubtype(),PyType_HasFeature()Py<type>_CheckとPy<type>_CheckExact。 例えばPyTuple_Check()
「DuringGC」関数¶
次の関数は tp_traverse ハンドラ内で のみ 使うべきです。
他の文脈で呼び出すと、意図しない結果を招く可能性があります。
これらの関数は _DuringGC サフィックスのない対応する関数と同様に動作しますが、副作用を起こさないことが保証され、失敗しても例外を設定せず、個別の説明に記載されているとおり 借用参照 を返す、または設定します。
これらの関数は失敗する (NULL または -1 を返す) ことがありますが、例外を設定しないのでエラー情報は得られません。
場合によっては、失敗と成功時の NULL の結果を区別できません。
-
void *PyObject_GetTypeData_DuringGC(PyObject *o, PyTypeObject *cls)¶
-
void *PyObject_GetItemData_DuringGC(PyObject *o)¶
-
void *PyType_GetModuleState_DuringGC(PyTypeObject *type)¶
-
void *PyModule_GetState_DuringGC(PyObject *module)¶
-
int PyModule_GetToken_DuringGC(PyObject *module, void **result)¶
- 次に属します: Stable ABI (バージョン 3.15 より).
共通の情報は 「DuringGC」関数 を参照してください。
Added in version 3.15.
-
int PyType_GetBaseByToken_DuringGC(PyTypeObject *type, void *tp_token, PyTypeObject **result)¶
- 次に属します: Stable ABI (バージョン 3.15 より).
共通の情報は 「DuringGC」関数 を参照してください。
*result には強参照ではなく 借用参照 を設定します。 この参照は
tp_traverseハンドラの呼び出し中ずっと有効です。Added in version 3.15.
-
PyObject *PyType_GetModule_DuringGC(PyTypeObject *type)¶
-
PyObject *PyType_GetModuleByToken_DuringGC(PyTypeObject *type, const void *mod_token)¶
- 戻り値: 借用参照。 次に属します: Stable ABI (バージョン 3.15 より).
共通の情報は 「DuringGC」関数 を参照してください。
これらの関数は 借用参照 を返します。 この参照は
tp_traverseハンドラの呼び出し中ずっと有効です。Added in version 3.15.
ガベージコレクタの状態を制御する¶
C API は、ガベージコレクションの実行を制御するために次の関数を提供します。
-
Py_ssize_t PyGC_Collect(void)¶
- 次に属します: Stable ABI.
ガベージコレクタが有効な場合に、完全なガベージコレクションを実行します (
gc.collect()は無条件に実行する点に注意してください)。回収したオブジェクト数と、到達不能なのに回収できないオブジェクト数の合計を返します。 ガベージコレクタが無効か、すでに回収を実行している場合は、直ちに
0を返します。 ガベージコレクション中のエラーはsys.unraisablehookに渡されます。 この関数は例外を送出しません。
-
int PyGC_Enable(void)¶
- 次に属します: Stable ABI (バージョン 3.10 より).
ガベージコレクタを有効にします。
gc.enable()に相当します。 以前の状態を返し、無効だった場合は 0、有効だった場合は 1 です。Added in version 3.10.
-
int PyGC_Disable(void)¶
- 次に属します: Stable ABI (バージョン 3.10 より).
ガベージコレクタを無効にします。
gc.disable()に相当します。 以前の状態を返し、無効だった場合は 0、有効だった場合は 1 です。Added in version 3.10.
-
int PyGC_IsEnabled(void)¶
- 次に属します: Stable ABI (バージョン 3.10 より).
ガベージコレクタの状態を問い合わせます。
gc.isenabled()に相当します。 現在の状態を返し、無効の場合は 0、有効の場合は 1 です。Added in version 3.10.
ガベージコレクタの状態を問い合わせる¶
C API は、ガベージコレクタの情報を問い合わせるために次のインターフェースを提供します。
-
void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg)¶
- これは Unstable APIです。マイナーリリースで予告なく変更されることがあります。
生存しているすべての GC 対応オブジェクトに対して、指定された callback を実行します。 callback を呼び出すたびに arg をそのまま渡します。
警告
コールバックがオブジェクトを割り当てたり解放したりした場合、それらのオブジェクトが巡回されるかどうかは未定義です。
処理中はガベージコレクションが無効になります。 コールバック内で明示的にコレクションを実行すると、同じオブジェクトを複数回巡回したり、まったく巡回しなかったりするなど、未定義の動作を引き起こす可能性があります。
Added in version 3.12.
-
typedef int (*gcvisitobjects_t)(PyObject *object, void *arg)¶
PyUnstable_GC_VisitObjects()に渡すビジター関数の型です。 arg はPyUnstable_GC_VisitObjectsに渡された arg と同じです。 反復処理を続けるには1、停止するには0を返してください。 その他の戻り値は現在予約されており、これら以外の値を返した場合の動作は未定義です。Added in version 3.12.