オブジェクトのライフサイクル

この節では、オブジェクトの生存期間を通じて型の各スロットがどのように関係するかを説明します。 各スロットの完全なリファレンスを意図したものではありません。 個々のスロットの詳細は 型オブジェクトの構造体 の該当する説明を参照してください。

生存期間中のイベント

次の図は、オブジェクトの生存期間中に起こり得るイベントの順序を示しています。 A から B への矢印は、イベント A の後にイベント B が起こり得ることを示します。 矢印のラベルは、A の後に B が起こるために必要な条件を表します。

Life Events tp_new tp_new start->tp_new    type call   tp_alloc tp_alloc tp_new->tp_alloc  direct call   tp_init tp_init tp_new->tp_init reachable reachable tp_init->reachable reachable->tp_init tp_traverse tp_traverse reachable->tp_traverse  not in a    cyclic    isolate   reachable->tp_traverse  periodic    cyclic isolate     detection   finalized? marked as finalized? reachable->finalized?  no refs   tp_finalize tp_finalize reachable->tp_finalize  resurrected    (maybe remove    finalized mark)   uncollectable uncollectable (leaked) reachable->uncollectable  cyclic    isolate    (no GC    support)   tp_dealloc tp_dealloc reachable->tp_dealloc  no refs tp_traverse->finalized?  cyclic    isolate   finalized?->tp_finalize  no (mark    as finalized)   tp_clear tp_clear finalized?->tp_clear  yes   tp_finalize->tp_clear  no refs or     cyclic isolate   tp_finalize->tp_dealloc  recommended  call (see  explanation) tp_finalize->tp_dealloc   no refs   tp_clear->uncollectable  cyclic    isolate   tp_clear->tp_dealloc  no refs   tp_free tp_free tp_dealloc->tp_free    direct call  

説明:

  • 型を呼び出して新しいオブジェクトを構築するとき:

    1. 新しいオブジェクトを作成するために tp_new が呼び出されます。

    2. tp_new が tp_alloc を直接呼び出し、新しいオブジェクトのメモリを割り当てます。

    3. tp_init が新しく作成されたオブジェクトを初期化します。 必要であれば tp_init を再度呼び出してオブジェクトを再初期化できます。 また、例えば Python コードから __new__() を呼び出す場合など、tp_init の呼び出しを完全に省くこともできます。

  • tp_init が完了すると、オブジェクトを使えるようになります。

  • オブジェクトへの最後の参照が取り除かれてからしばらく後:

    1. オブジェクトに ファイナライズ済み の印が付いていない場合、ファイナライズ済み として印を付けて tp_finalize 関数を呼び出すことにより、ファイナライズされる可能性があります。 Python は最後の参照が削除されたときにオブジェクトをファイナライズするわけでは ありません。 tp_finalize が必ず呼び出されるようにするには、PyObject_CallFinalizerFromDealloc() を使ってください。

    2. オブジェクトにファイナライズ済みの印が付いている場合、ガベージコレクタが tp_clear を呼び出して、オブジェクトの保持する参照をクリアする可能性があります。 オブジェクトの参照カウントがゼロになったときに呼び出されるわけでは ありません。

    3. オブジェクトを破棄するために tp_dealloc が呼び出されます。 コードの重複を避けるため、tp_dealloc は通常 tp_clear を呼び出してオブジェクトの参照を解放します。

    4. tp_dealloc はオブジェクトの破棄を終えると、tp_free を直接呼び出してメモリを解放します。 このスロットには通常、型に応じて PyObject_Free() または PyObject_GC_Del() が自動的に設定されます。

  • tp_finalize 関数は、必要に応じてオブジェクトへの参照を追加できます。 参照を追加するとオブジェクトが 復活 し、予定されていた破棄が阻止されます。 オブジェクトを復活させられるのは tp_finalize だけです。 tp_clear と tp_dealloc は、tp_finalize を呼び出さずに復活させることはできません。 復活したオブジェクトの ファイナライズ済み の印は、取り除かれる場合も残る場合もあります。 現在の Python は、ガベージコレクションをサポートするオブジェクト (Py_TPFLAGS_HAVE_GC フラグが設定されているもの) が復活した場合は ファイナライズ済み の印を残し、サポートしない場合は印を取り除きます。 これらの動作の一方または両方は、将来変更される可能性があります。

  • tp_dealloc は、オブジェクトの破棄処理にコードを再利用したい場合、PyObject_CallFinalizerFromDealloc() を介して tp_finalize を呼び出すことができます。 これによって破棄前に必ず tp_finalize が呼び出されるので、この方法を推奨します。 コード例は tp_dealloc の説明を参照してください。

  • オブジェクトが 孤立した循環参照群 の一部であり、tp_clear が循環参照を解消できないか、その循環参照群が検出されない場合、オブジェクトはいつまでも回収できずに残ります (「リーク」します)。 検出されない原因としては、gc.disable() が呼び出された場合や、関連する型のいずれかで誤って Py_TPFLAGS_HAVE_GC フラグが省かれた場合などが考えられます。 gc.garbage を参照してください。

オブジェクトがガベージコレクションをサポートすると示されている場合 (tp_flags に Py_TPFLAGS_HAVE_GC フラグが設定されている場合)、次のイベントも起こり得ます。

  • ガベージコレクタは、ときどき tp_traverse を呼び出して 孤立した循環参照群 を特定します。

  • ガベージコレクタは 孤立した循環参照群 を発見すると、その群のオブジェクトの 1 つに ファイナライズ済み の印を付け、tp_finalize 関数があれば呼び出して、ファイナライズします。 これを、孤立した循環参照群がなくなるか、すべてのオブジェクトがファイナライズされるまで繰り返します。

  • tp_finalize は、孤立した循環参照群 の外から参照を追加することで、オブジェクトを復活させられます。 新しい参照が加わると、その群は孤立した循環参照群ではなくなります (循環参照が残っていても、オブジェクトはもはや孤立していません)。

  • ガベージコレクタが 孤立した循環参照群 を発見し、その群のすべてのオブジェクトに ファイナライズ済み の印が付いている場合、 まだクリアされていないオブジェクトのうち 1 つ以上について、それぞれの tp_clear 関数を呼び出してクリアします (並行して行われる場合もあります)。 孤立した循環参照群が残っており、まだすべてのオブジェクトがクリアされていない間、この処理を繰り返します。

孤立した循環参照群の破棄

以下では、各オブジェクトをファイナライズまたはクリアした後も残り続けると仮定した 孤立した循環参照群 の段階を示します。 孤立した循環参照群がすべての段階を通過するなら、それはメモリリークです。 遅くともすべてのオブジェクトをクリアした時点で、孤立した循環参照群はなくなるべきです。 循環参照が解消されるか、ファイナライザによる復活によってオブジェクトが孤立しなくなると、孤立した循環参照群はなくなります (tp_finalize を参照)。

  1. 到達可能 (まだ孤立した循環参照群ではありません): すべてのオブジェクトが通常の到達可能な状態にあります。 循環参照があっても、外部からの参照があるのでまだ孤立していません。

  2. 到達不能だが整合性は保たれている: 循環参照しているオブジェクト群への外部からの最後の参照が取り除かれ、オブジェクトが孤立した状態です (これによって孤立した循環参照群が生じます)。 群のどのオブジェクトも、まだファイナライズもクリアもされていません。 孤立した循環参照群は、将来ガベージコレクタが実行されるまでこの段階にとどまります (次の実行で全オブジェクトを調べるとは限らないため、必ずしも次回とは限りません)。

  3. ファイナライズ済みと未ファイナライズが混在: 孤立した循環参照群のオブジェクトは 1 つずつファイナライズされるため、ファイナライズ済みとそうでないオブジェクトが混在する期間があります。 ファイナライズの順序は規定されていないため、無作為に見えることがあります。 ファイナライズ済みオブジェクトは、未ファイナライズのオブジェクトから操作されても適切に動作しなければなりません。 また、未ファイナライズのオブジェクトは、自分の参照先の任意の一部がファイナライズされても問題なく扱える必要があります。

  4. すべてファイナライズ済み: 孤立した循環参照群では、どのオブジェクトもクリアされる前に、すべてのオブジェクトがファイナライズされます。

  5. ファイナライズ済みとクリア済みが混在: オブジェクトは逐次的にも並行的にもクリアされ得ますが、GIL を保持して行われます。 いずれの場合も、他のオブジェクトより先にクリアが完了するものがあります。 ファイナライズ済みオブジェクトは、自分の参照先の一部がクリアされても問題なく扱える必要があります。 PEP 442 ではこの段階を「cyclic trash」と呼んでいます。

  6. リーク: 群のすべてのオブジェクトをファイナライズし、クリアした後も孤立した循環参照群が残る場合、それらのオブジェクトはいつまでも回収できずに残ります (gc.garbage を参照)。 孤立した循環参照群がこの段階に達するのはバグです。 関与するオブジェクトの tp_clear メソッドが、必要な循環参照の解消に失敗したことを意味します。

tp_clear がなければ、Python には循環参照を安全に解消する方法がありません。 孤立した循環参照群のオブジェクトを単に破棄すると、ダングリングポインタが生じます。 破棄済みのオブジェクトを参照するオブジェクトが破棄される際に、未定義の動作を引き起こします。 クリアの段階を設けることで、オブジェクトの破棄は 2 段階になります。 まず tp_clear を呼び出し、オブジェクト同士の結び付きを解くのに十分なだけ部分的に破棄します。 次に tp_dealloc を呼び出し、破棄を完了します。

クリアとは異なり、ファイナライズは破棄の一段階ではありません。 ファイナライズ済みのオブジェクトも、設計上の契約を引き続き満たし、正しく動作しなければなりません。 オブジェクトのファイナライザは任意の Python コードを実行でき、参照を追加して、間近に迫った破棄を防ぐことさえできます。 ファイナライザと破棄が関係するのは呼び出し順序だけです。 ファイナライザが実行されるなら、それは破棄より前です。 破棄は tp_clear (呼び出す場合) から始まり、tp_dealloc で完了します。

孤立した循環参照群のオブジェクトを安全に回収するために、ファイナライズの段階は必須ではありません。 ただし、この段階があることで、オブジェクトがクリアされても適切に動作する型を設計しやすくなります。 オブジェクトのクリアでは、部分的に破棄された壊れた状態を避けられない場合があります。 クリア済みのオブジェクトのメソッド呼び出しや属性へのアクセスは、どれも安全でないかもしれません。 ファイナライズの段階があれば、クリア済みのオブジェクトとやり取りする可能性があるのはファイナライズ済みのオブジェクトだけです。 未ファイナライズのオブジェクトがやり取りする相手は、未クリア (ただしファイナライズ済みの場合もあります) のオブジェクトだけであると保証されます。

起こり得る参照関係をまとめると、次のようになります。

  • 未ファイナライズのオブジェクトは、未ファイナライズまたはファイナライズ済みのオブジェクトへの参照や、それらからの参照を持つことがあります。 クリア済みオブジェクトとの間には、そのような参照はありません。

  • ファイナライズ済みのオブジェクトは、未ファイナライズ、ファイナライズ済み、クリア済みのいずれのオブジェクトへの参照や、それらからの参照も持ち得ます。

  • クリア済みのオブジェクトは、ファイナライズ済みまたはクリア済みのオブジェクトへの参照や、それらからの参照を持つことがあります。 未ファイナライズのオブジェクトとの間には、そのような参照はありません。

循環参照がなければ、最後の参照が削除された時点でオブジェクトを単に破棄できます。 使われなくなったオブジェクトを安全に回収するために、ファイナライズやクリアの段階は必要ありません。 それでも、破棄前に tp_finalize と tp_clear を自動的に呼び出すことには利点があります。 孤立した循環参照群に属していたかどうかにかかわらず、すべてのオブジェクトが常に同じ一連のイベントを経るようにすると、型の設計が簡単になるためです。 現在の Python は、孤立した循環参照群の破棄に必要な場合にだけ tp_finalize と tp_clear を呼び出します。 これは将来のバージョンで変更される可能性があります。

関数

メモリの割り当てと解放については ヒープ上でのオブジェクトの割り当て を参照してください。

void PyObject_CallFinalizer(PyObject *op)

tp_finalize の説明に従ってオブジェクトをファイナライズします。 tp_finalize を直接呼び出すのではなく、この関数か PyObject_CallFinalizerFromDealloc() を呼び出してください。 この関数は tp_finalize の重複する呼び出しを省く場合があるためです。 現在は、型がガベージコレクションをサポートする場合 (Py_TPFLAGS_HAVE_GC フラグが設定されている場合) にだけ重複を省きますが、将来変更される可能性があります。

Added in version 3.4.

int PyObject_CallFinalizerFromDealloc(PyObject *op)
次に属します: Stable ABI (バージョン 3.15 より).

PyObject_CallFinalizer() と同じですが、オブジェクトのデストラクタ (tp_dealloc) の冒頭で呼び出すための関数です。 オブジェクトへの参照が残っていてはいけません。 ファイナライザがオブジェクトを復活させた場合は -1 を返します。 この場合、これ以上破棄を進めてはいけません。 それ以外の場合は 0 を返し、通常どおり破棄を続行できます。

Added in version 3.4.

参考

コード例は tp_dealloc を参照してください。