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_a
がnode_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())
memory_usage
関数は、Pythonのオブジェクトのサイズを取得し、メモリ使用量を表示します。create_circular_reference
関数は、循環参照を持つ2つのノードを生成します。このコードを実行すると、循環参照を作成した後のメモリ使用量が増加し、ガベージコレクションを実行しても減少しないことが確認できるはずです。このことが、循環参照がメモリリークを引き起こす理由を示しています。
このように、循環参照はメモリ管理において注意が必要なポイントであり、適切に対処することが重要です。次のトピックでは、ガベージコレクタによる循環参照の検出方法について学んでいきましょう。