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

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


生存期間中のイベント
====================

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

[画像: オブジェクトの生存期間中のイベントを示す図。 詳細は後述します。
][画像]

説明:

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

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

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

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

* **リーク:** 群のすべてのオブジェクトをファイナライズし、クリアした後
  も孤立した循環参照群が残る場合、それらのオブジェクトはいつまでも回収
  できずに残ります ("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" を参照してください。
