Pythonのごみ回収メカニズム(garbage collection/gc)を簡単に述べる

11063 ワード

最近面接でpythonのgcを聞かれて、自分がよく知らないと感じて、ここで勉強とまとめをします.
Pythonのゴミ回収メカニズム
  • Pythonのゴミ回収メカニズム
  • 参照カウント
  • 参照カウントを増加する場合
  • .
  • 参照カウントを減少する場合
  • .
  • 参照カウントの長所と短所
  • 分代回収
  • トリガ条件
  • 弱代仮説
  • Pythonのゴミ回収メカニズム
    まずpython docのgarbage collectionの原文を貼ります
    CPythonは現在、(オプション)遅延検出ループ・リンク・ゴミ付きリファレンスカウント・スキームを使用しており、オブジェクトがアクセスできない場合にその大部分をすぐに回収しますが、ループ・リファレンスを含むゴミの回収は保証されていません.
    pythonは主に参照カウント(reference counting)を用いてごみ回収を処理しているが、循環参照のような参照カウントで解決できない場合、pythonはマーククリア(mark-sweep)を用いた世代回収(generational collection)を採用している.
    参照数
    Pythonはオブジェクト向けのスクリプト言語であることを知っています.Pythonのすべてのもの、例えば数字、文字列などはすべてオブジェクトであり、そのコアにはPyObjectという構造体が含まれています.
    typedef struct_object{
    
      int ob_refcnt;
    
      struct_typeobject *ob_type;
    
    }PyObject;
    
    #define Py_INCREF(op)   ((op)->ob_refcnt++)          //    
    #define Py_DECREF(op)      \                         //            
         if (--(op)->ob_refcnt != 0)    \
             ;        \
         else         \
             __Py_Dealloc((PyObject *)(op))
    

    ob_refcntは参照カウントです.オブジェクトに新しい参照がある場合、そのob_refcntは増加し、参照オブジェクトが消えるとob_refcntは減少します.参照数が0の場合、オブジェクトのライフは終了します.
    参照数を増やす場合
  • オブジェクトの作成
  • >>> class foo:
    ... 	pass
    ...
    >>> sys.getrefcount(foo())
    1
    
  • オブジェクトの参照
  • >>> a = foo()
    >>> sys.getrefcount(a)
    2
    
  • オブジェクトはパラメータとして関数中の
  • に入る.
    >>> a = [0]
    >>> sys.getrefcount(a)
    2
    >>> def f1(x):
    ...     x.append(0)
    ...     return x
    ... 
    >>> f1(a)
    [0, 0]
    >>> sys.getrefcount(a)
    3
    
  • オブジェクトは、要素としてコンテナに格納する
  • .
    >>> import sys
    >>> a = 789
    >>> sys.getrefcount(a)
    2
    >>> b = [a]
    >>> sys.getrefcount(a)
    3
    >>> c = (a,)
    >>> sys.getrefcount(a)
    4
    

    参照数を減らす場合
    上記の場合と同様に、参照カウントを減らす場合はそれぞれ
  • オブジェクトの別名が明示的に破棄する場合、例えばdel a
  • .
  • オブジェクトのエイリアスが新しいオブジェクト、例えばa=10
  • に付与されると、
  • オブジェクトがその役割ドメインから離れたとき.例えばfunc関数の実行が完了すると、関数内の局所変数の参照カウンタが1減少する(ただしグローバル変数はできない)
  • .
  • 要素が容器から削除されたとき、または容器が破棄されたとき.

  • 参照カウントのメリットとデメリット
    オブジェクトへのメモリの参照カウンタが0の場合、そのメモリはPython仮想マシンによって破棄されます.リファレンスカウントは、ほとんどのゴミ回収の問題を解決することができます.メリット
  • リアルタイム:参照がなければ、メモリは直接解放されます.他のメカニズムのように特定のタイミングを待つ必要はありません.リアルタイム性には、回収メモリの処理時間が通常の
  • に割り当てられるというメリットもあります.
  • 論理的には簡単です.統計回数だけでいいです.
  • をよく理解しています.
    欠点
  • リソース消費:Pythonは、参照数を処理するために各オブジェクトの内部にいくつかの空間を残さなければならない.このようにして少しの空間的な代価を払った.しかし、さらに悪いことに、変数や参照を変更するなどの簡単な操作は、Pythonがカウントを増やし、もう1つを減らし、オブジェクトを解放する必要があるため、より複雑な操作になります.
  • 速度が遅い:Pythonはプログラムに従ってGCを実行するのは安定しているが、必ずしも速くはない.Pythonは多くの参照数値を更新し続けている.特に、大きなデータ構造を使用しなくなった場合、例えば多くの要素を含むリストでは、Pythonは一度に多くのオブジェクトを解放する必要があります.参照数を減らすことは複雑な再帰プロセスになります.
  • ループリファレンスの問題を解決できません:
    list1 = []
    list2 = []
    list1.append(list2)
    list2.append(list1)
    
    コードがこのような場合、リファレンスカウントを使用してこの2つのlistをクリアすることはできません.

  • ぶんかつかいしゅう
    Pythonは、アクティブなオブジェクトを追跡し続けるためにチェーンテーブルを使用します.Pythonの内部Cコードはこれをゼロ世代(GenerationZero)と呼ぶ.オブジェクトや他の値を作成するたびに、Pythonはゼロ世代チェーンテーブルに追加します.
    その後、Pythonはゼロ世代リストの各オブジェクトをループし、リスト内の相互参照の各オブジェクトをチェックし、ルールに従って参照カウントを減らします.この過程で、Pythonは、オブジェクトを早期に解放しないように、内部参照の数を次々と統計します.
    トリガ条件
    プログラムが実行されると、Pythonインタプリタは、新しく作成されたオブジェクトと参照カウントがゼロで解放されたオブジェクトの追跡を維持します.理論的には、プログラムが新しく作成された各オブジェクトが最終的に解放されるべきであるため、この2つの値は一致しなければならない.
    しかし、実際には、ループまたはプログラムが他のオブジェクトよりも長い時間存在するオブジェクトを使用しているため、割り当てられたオブジェクトのカウント値と解放されたオブジェクトのカウント値の差は徐々に増加している.差異が累計しきい値を超えると、Pythonの収集メカニズムが起動し、上記のゼロ世代アルゴリズムをトリガーして「フローティングゴミ」を解放し、残りのオブジェクトを世代リストに移動します.
    時間が経つにつれて,プログラムが使用するオブジェクトは次第にゼロ世代リストから世代リストに移動する.一方、Pythonは、世代リスト内のオブジェクトの処理についても同様の方法に従い、割り当てられたカウント値が解放されたカウント値と累積して一定の閾値に達すると、Pythonは残りのアクティブなオブジェクトを2世代リストに移動する.
    この方法では、あなたのコードが長期にわたって使用されているオブジェクト、あなたのコードがアクセスし続けるアクティブなオブジェクトは、ゼロ世代チェーンテーブルから世代に移行し、2世代に移行します.異なるしきい値設定により、Pythonは異なる時間間隔でこれらのオブジェクトを処理することができる.Pythonはゼロ世代を処理するのが最も頻繁で、次は世代で、次は2世代です.
    弱代仮説
    弱い仮説は2つの観点から構成されている:若い対象は通常死ぬのも速く、古い対象はもっと長く生きる可能性が高い.
    代ごみ回収アルゴリズムの核心的な行為:ごみ回収器は新しいオブジェクトをより頻繁に処理します.新しいオブジェクトはプログラムが作成したばかりで、来たオブジェクトはいくつかの時間サイクルを経ても存在するオブジェクトです.
    Pythonは、オブジェクトがゼロから世代に移動したり、世代から2世代に移動したりする過程で、このオブジェクトを昇格させます.このため、弱世代仮説です.
    古い世代のオブジェクトは、最も長く生存していたオブジェクトであり、システム全体のライフサイクル内に生存していた.
    参考資料:[1]:https://blog.csdn.net/anmi3721/article/details/101666340?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase [2]: https://www.cnblogs.com/xiugeng/p/10514101.html#_label0_1 [3]: https://blog.csdn.net/chaxiaoli001/article/details/104048926?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-2 [4]: https://blog.csdn.net/anmi3721/article/details/101666340?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase