<一覧に戻る

循環参照がメモリリークを引き起こす理由

Pythonには、自動的にメモリを管理するためのガベージコレクション機能がありますが、循環参照が発生すると、メモリリークの原因となることがあります。本教材では、循環参照とそれがメモリリークを引き起こす理由について、サンプルコードを用いて説明します。

循環参照とは

循環参照とは、2つ以上のオブジェクトが互いに参照し合っている状態を指します。このような場合、オブジェクトが消滅しない限り、それらのオブジェクトがメモリに留まり続けることがあります。

例: 循環参照の発生

以下のサンプルコードでは、Nodeクラスのインスタンスが互いに参照し合う状況を作成しています。

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

# 循環参照の生成
node_a = Node("A")
node_b = Node("B")

node_a.next_node = node_b
node_b.next_node = node_a  # 循環参照を作成

このコードでは、node_anode_bを参照し、node_bが再びnode_aを参照しています。この状態では、どちらのノードもガベージコレクションによって解放されることはありません。

メモリリークの原因

上記の循環参照が存在する場合、Pythonのリファレンスカウントは、各オブジェクトの参照数を追跡しますが、循環参照により参照数が0にならない限り、オブジェクトはメモリから解放されません。このため、以下のような問題が発生します。

  • メモリの無駄遣い: 不要なオブジェクトがメモリに残り続け、使用可能なメモリが減少します。
  • アプリケーションのパフォーマンスの低下: メモリが不足すると、アプリケーションのパフォーマンスが低下することがあります。

メモリリークの確認

次に、実際に循環参照がメモリリークを引き起こす様子を確認するためのサンプルコードを用意しました。以下のコードでは、ガベージコレクションの前後でメモリ使用量を確認します。

import gc
import sys

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

# メモリ使用量を表示する関数
def memory_usage():
    return sys.getsizeof(gc.get_objects())

# 循環参照の生成
def create_circular_reference():
    node_a = Node("A")
    node_b = Node("B")
    node_a.next_node = node_b
    node_b.next_node = node_a  # 循環参照を作成
    return node_a, node_b

# メモリ使用量の確認
print("Before creating circular reference:", memory_usage())
create_circular_reference()
print("After creating circular reference:", memory_usage())

# ガベージコレクションの実行
gc.collect()
print("After garbage collection:", memory_usage())

コードの解説

  1. メモリ使用量の表示: memory_usage関数は、Pythonのオブジェクトのサイズを取得し、メモリ使用量を表示します。
  2. 循環参照の生成: create_circular_reference関数は、循環参照を持つ2つのノードを生成します。
  3. メモリ使用量の確認: 循環参照を作成する前後と、ガベージコレクションを実行した後のメモリ使用量を表示します。

実行結果の期待

このコードを実行すると、循環参照を作成した後のメモリ使用量が増加し、ガベージコレクションを実行しても減少しないことが確認できるはずです。このことが、循環参照がメモリリークを引き起こす理由を示しています。

まとめ

  • 循環参照は、オブジェクトが互いに参照し合うことで発生します。
  • 循環参照により、リファレンスカウントが0にならず、オブジェクトがメモリから解放されないため、メモリリークが発生することがあります。
  • メモリ使用量を確認することで、循環参照の影響を視覚的に理解できるようになります。

このように、循環参照はメモリ管理において注意が必要なポイントであり、適切に対処することが重要です。次のトピックでは、ガベージコレクタによる循環参照の検出方法について学んでいきましょう。

出力結果: