web-dev-qa-db-ja.com

Pythonのガベージコレクターは循環参照をどのように検出しますか?

Pythonのガベージコレクターが循環参照を検出する方法を理解しようとしています。ドキュメントを見ると、関係するオブジェクトに__del__メソッドがある場合を除いて、循環参照が検出されたというステートメントだけが表示されます。

これが発生した場合、私の理解(おそらく障害)は、gcモジュールが、割り当てられたすべてのメモリをウォークスルーし、到達不能なブロックを解放することによって、フェイルセーフとして機能することです。

Python gcモジュールを使用する前に、循環メモリ参照をどのように検出して解放しますか?

33
user1245262

元の質問へのコメントで@SvenMarnichによって提供されたいくつかのリンクで私が探している答えを見つけたと思います:

コンテナオブジェクトはPython他のPythonオブジェクトへの参照を保持できるオブジェクトです。リスト、クラス、タプルなどはコンテナオブジェクトですが、整数、文字列などはそうではありません。したがって、コンテナオブジェクトのみが循環参照に含まれるリスクがあります。

各Pythonオブジェクトにはフィールドがあります-* gc_ref *、これは(私が信じる)非コンテナオブジェクトの場合はNULLに設定されます。コンテナオブジェクトの場合はそれを参照する非コンテナオブジェクト

* gc_ref *カウントが1より大きいコンテナオブジェクト(?私は0と思っていたでしょうが、今は大丈夫ですか?)には、コンテナオブジェクトではない参照があります。したがって、それらは到達可能であり、到達不可能なメモリアイランドであるという考慮から除外されます。

到達可能であることがわかっているオブジェクト(つまり、* gc_ref *カウントが1より大きいと認識したオブジェクト)が到達可能なコンテナオブジェクトも、解放する必要はありません。

残りのコンテナオブジェクトは(お互いを除いて)到達できないため、解放する必要があります。

http://www.arctrix.com/nas/python/gc/ は、より完全な説明を提供するリンクです http://hg.python.org/cpython/file/2059910e7d76/ Modules/gcmodule.c はソースコードへのリンクであり、循環参照検出の背後にある考えをさらに説明するコメントがあります

5
user1245262

Python gcモジュールを使用する前に、循環メモリ参照をどのように検出して解放しますか?

そうではありません。 gcはto循環参照を検出して解放するだけです。非循環参照は、再カウントによって処理されます。

ここで、how gcが特定のオブジェクトによって参照されるオブジェクトのセットを決定することを確認するには、gc_get_referencesModules/gcmodule.c関数を見てください。関連するビットは次のとおりです。

// Where `obj` is the object who's references we want to find
traverseproc traverse;
if (! PyObject_IS_GC(obj))
    continue;
traverse = Py_TYPE(obj)->tp_traverse;
if (! traverse)
    continue;
if (traverse(obj, (visitproc)referentsvisit, result)) {
    Py_DECREF(result);
    return NULL;
}

ここでの主な機能はtp_traverseです。各Cレベルのタイプは、tp_traverse関数を定義します(または、strのように参照を保持しないオブジェクトの場合は、それをNULLに設定します)。 tp_traverseの一例はlist_traverselistのトラバーサル関数です。

static int
list_traverse(PyListObject *o, visitproc visit, void *arg)
{
    Py_ssize_t i;

    for (i = Py_SIZE(o); --i >= 0; )
        Py_VISIT(o->ob_item[i]);
    return 0;
}

関係するオブジェクトに__del__()メソッドがある場合を除いて、循環参照が検出されるというステートメントがあります。

正解です— Pythonのサイクル検出器はサイクルを検出して収集できますただしインタプリタがこれらのオブジェクトを安全に削除する方法がないため(これがなぜであるかを直感的に理解するために)__del__メソッドを持つオブジェクトが含まれている、相互に参照する__del__メソッドを持つ2つのオブジェクトがあると想像してください。それらはどの順序で解放する必要がありますか?)。

__del__メソッドを持つオブジェクトがサイクルに関与している場合、ガベージコレクターはそれらを別のリストに貼り付け( gc.garbage からアクセス可能)、プログラマーが手動でそれらを「処理」できるようにします。

26
David Wolever

Python gcモジュールを使用する前に、循環メモリ参照をどのように検出して解放しますか?

Pythonのガベージコレクター(実際には、ガベージコレクターへのPythonインターフェイス)であるgcモジュールではありません)がこれを行います。したがって、Python しませんガベージコレクタを使用する前に、循環メモリ参照を検出して解放します。

Pythonは通常、参照カウントがゼロに達するとすぐにほとんどのオブジェクトを解放します。 (たとえば、小さな整数やインターン文字列などが解放されないため、「ほとんど」と言います。)循環参照の場合、これは決して発生しないため、ガベージコレクターは定期的にメモリをウォークし、循環参照オブジェクトを解放します。

もちろん、これはすべてCPython固有です。その他のPython実装では、メモリ管理が異なります(Jython = Java VM、IronPython = Microsoft .NET CLR))。

5
kindall