40

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

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

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

4

3 に答える 3

34

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

そうではありません。gc は、循環参照を検出して解放するためだけに存在します。非循環参照は、refcounting によって処理されます。

ここで、特定のオブジェクトが参照するオブジェクトのセットを gc がどのようgc_get_referencesに決定するかを確認するには、 の関数を見てくださいModules/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)。の 1 つの例tp_traverselist_traverse、次のトラバーサル関数ですlist

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__。これは、インタープリターがこれらのオブジェクトを安全に削除する方法がないためです (理由を直感的に理解するために、2 つのオブジェクトがあると想像してください)。__del__相互に参照するメソッドで解放する必要があります。どの順序で解放する必要がありますか?)

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

于 2012-06-09T16:05:59.650 に答える
7

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

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

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

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

于 2012-06-09T16:06:32.320 に答える
7

元の質問へのコメントで@SvenMarnichが提供するいくつかのリンクで、探している答えを見つけたと思います。

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

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

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

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

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

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

于 2012-06-09T17:38:16.493 に答える