Pythonは、メモリ管理を行うためにガベージコレクションとリファレンスカウントの2つの主要な仕組みを使用しています。リファレンスカウントは、オブジェクトが何回参照されているかを追跡する方法であり、ガベージコレクションは、使用されていないオブジェクトを自動的に回収する仕組みです。この教材では、リファレンスカウントとガベージコレクションの違いを理解し、それぞれの役割を把握するためのサンプルコードを示します。
リファレンスカウント方式では、各オブジェクトはそのオブジェクトへの参照の数を持っています。参照が増えるとカウントが増加し、参照が減るとカウントが減少します。カウントがゼロになると、そのオブジェクトはメモリから解放されます。
以下のコードは、リファレンスカウントの簡単な例を示しています。
class MyObject:
def __init__(self, name):
self.name = name
print(f"{self.name} created")
def __del__(self):
print(f"{self.name} deleted")
# リファレンスカウントの確認
obj1 = MyObject("Object1") # カウント1
obj2 = obj1 # カウント2
print(f"Reference count for obj1: {obj1.__dict__}")
# obj2を削除
del obj2 # カウント1
print(f"Reference count for obj1: {obj1.__dict__}")
# obj1を削除
del obj1 # カウント0
MyObject
クラスを定義し、__init__
メソッドでオブジェクトが生成されたことを通知します。__del__
メソッドでオブジェクトが削除されたことを通知します。obj1
を生成すると、リファレンスカウントは1になります。obj2
がobj1
を参照すると、リファレンスカウントは2になります。del obj2
を実行すると、obj1
への参照が1つ減ります。del obj1
を実行すると、リファレンスカウントが0になり、オブジェクトが削除されます。リファレンスカウントは、オブジェクトのメモリ管理を行う一つの方法ですが、循環参照が存在する場合には効果がありません。循環参照とは、2つ以上のオブジェクトが互いに参照し合っている状態です。このような場合、リファレンスカウントではカウントがゼロにならず、オブジェクトが解放されないため、メモリリークが発生します。
以下のサンプルコードは、循環参照がリファレンスカウントに与える影響を示します。
class Node:
def __init__(self, name):
self.name = name
self.next = None
print(f"{self.name} created")
def __del__(self):
print(f"{self.name} deleted")
# 循環参照の例
node1 = Node("Node1")
node2 = Node("Node2")
node1.next = node2
node2.next = node1 # 循環参照を作成
del node1
del node2 # この時点で、Node1とNode2はメモリから解放されません
Node
クラスを定義し、__init__
メソッドでオブジェクトが生成されたことを通知します。node1
とnode2
を生成し、互いに参照し合う循環参照を作成します。del node1
とdel node2
を実行しますが、循環参照のため、両方のオブジェクトはメモリから解放されません。リファレンスカウントは、オブジェクトがどれだけ参照されているかを追跡するシンプルなメモリ管理手法ですが、循環参照の問題に対処できないため、Pythonではガベージコレクションの仕組みも併用されています。ガベージコレクションは、使用されていないオブジェクトを自動的に回収し、メモリの効率的な管理を可能にします。このように、リファレンスカウントとガベージコレクションは、Pythonのメモリ管理において重要な役割を果たしています。