<一覧に戻る

ガベージコレクタによる循環参照の検出方法

Pythonでは、オブジェクトのメモリ管理にガベージコレクションが用いられています。特に、循環参照はガベージコレクタにとって難しい問題の一つです。この教材では、循環参照の検出方法について学びます。

循環参照とは

循環参照は、オブジェクトが互いに参照し合っている状態を指します。これにより、オブジェクトがメモリから解放されない場合があります。例えば、オブジェクトAがオブジェクトBを参照し、オブジェクトBがオブジェクトAを参照しているとき、これらのオブジェクトは循環参照を形成します。

ガベージコレクタの役割

Pythonのガベージコレクタは、使用されていないメモリを自動的に解放するために設計されています。通常、リファレンスカウントを用いてオブジェクトの使用状況を追跡しますが、循環参照がある場合、リファレンスカウントがゼロにならず、オブジェクトが解放されないことがあります。

循環参照の検出を確認する

次に、Pythonのgcモジュールを使用して、循環参照を検出できることを確認します。

import gc

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

    def __del__(self):
        pass  # 解放を妨げるカスタムデストラクタ

# 循環参照を作成
node1 = Node(1)
node2 = Node(2)
node1.next = node2
node2.next = node1

# 循環参照の解放を確認
gc.set_debug(gc.DEBUG_LEAK)  # ガベージコレクタのデバッグ情報を有効に
gc.collect()

print("循環参照の数:", len(gc.garbage))

コードの解説

  1. gcモジュールのインポート:

    • Python標準のgcモジュールをインポートし、ガベージコレクタを制御する機能を利用します。
  2. Nodeクラスの定義:

    • 値を保持するvalue属性と次のノードを参照するnext属性を持つクラスです。
    • __del__メソッドがカスタム定義されていますが、具体的な処理をせず、解放を妨げる役割を果たします。このため、循環参照をガベージコレクタが解決できなくなります。
  3. 循環参照の作成:

    • node1node2の2つのインスタンスを作成し、互いのnext属性を使って相互参照させます。
    • これにより、循環参照が構築されます。
  4. ガベージコレクタの動作設定:

    • gc.set_debug(gc.DEBUG_LEAK)により、ガベージコレクタのデバッグモードを有効化します。これにより、収集されなかったオブジェクトの詳細情報が出力されます。
  5. ガベージコレクタの手動実行:

    • gc.collect()を実行して、不要なオブジェクトの収集を試みます。
    • 解放できない循環参照オブジェクトがgc.garbageに残されます。
  6. 循環参照の検出:

    • gc.garbageの長さを確認し、解放できなかったオブジェクトの数を出力します。
    • 解放できない理由は、Nodeクラスの__del__メソッドが存在するためです。このようなケースではガベージコレクタが安全にオブジェクトを削除できないため、gc.garbageに残されます。

まとめ

Pythonのガベージコレクタは、循環参照を検出するためにトレーシング方式を使用しています。この教材では、循環参照の基本と、それを検出するためのサンプルコードを示しました。次回は、循環参照の回収プロセスについて学びます。

出力結果: