ガベージコレクターと循環参照
gc モジュールが参照サイクルを処理する方法を学びます。
「ガベージコレクターと循環参照」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPython Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Python Academyコースには全4レッスンが含まれています。
参照カウントだけでは不十分な理由
参照カウントでは、参照サイクルに含まれるオブジェクトを回収できません。互いに参照を保持するため、どちらのカウントも0にならないからです。
# Reference cycle: a → b → a
a = {}
b = {"other": a}
a["other"] = b
del a, b
# Both objects are unreachable but refcount > 0
# Only the cyclic GC can collect themgcモジュール
gc は循環ガベージコレクターです。追跡対象のオブジェクトを定期的にスキャンして到達不能なサイクルを探し、それらを回収します。
import gc
gc.collect() # trigger immediately
print(gc.garbage) # objects with __del__ in cycles (cannot auto-collect)世代別コレクション
GCは3つの世代を使用します。新しいオブジェクトは世代0から始まり、コレクション後も生存したオブジェクトは昇格します。世代0は最も頻繁に収集されます。
import gc
print(gc.get_count()) # (gen0, gen1, gen2) allocations since last collection
print(gc.get_threshold()) # (700, 10, 10) — thresholds for each generation
gc.set_threshold(1000, 15, 10) # tunegc.get_referentsによるサイクルの検出
あるオブジェクトが参照しているオブジェクトを調べます。何かが回収されない理由を理解するのに役立ちます。
import gc
a = []
b = [a]
a.append(b)
for ref in gc.get_referents(a):
print(ref) # shows bgc.get_objects()
gc.get_objects() は、現在GCによって追跡されているすべてのオブジェクトを返します。メモリリークの検出に役立ちます。
import gc
before = len(gc.get_objects())
create_lots_of_objects()
after = len(gc.get_objects())
print(f"Leaked: {after - before} objects")サイクルの回避
データ構造を設計する際は、サイクルを避けてください。逆参照にはweakrefsを使用し、直接参照の代わりにIDを使用するか、オブジェクトがスコープ外になる前に明示的にサイクルを解除します。
import weakref
class Node:
def __init__(self, parent):
# weakref avoids cycle:
self.parent = weakref.ref(parent)
class Tree:
def __init__(self):
self.child = Node(self) # no hard cycle__del__とサイクル
サイクルに含まれる __del__ を持つオブジェクトは自動的に回収できません。代わりに gc.garbage に置かれます。
import gc
class Leaky:
def __del__(self): pass
a = Leaky()
b = Leaky()
a.other = b
b.other = a
del a, b
gc.collect()
print(gc.garbage) # [Leaky, Leaky] — cannot collectgc.freeze() — Python 3.7以降
gc.freeze() は現在追跡されているオブジェクトを固定し、それらが回収されないようにします。起動後の長時間実行プロセスで使用すると、GCによる一時停止を減らせます。
import gc
# After all globals and imports are in place:
gc.freeze() # no GC scanning for these objects ever again
# Useful for: gunicorn pre-fork, background serversGCの無効化と再有効化
サイクルが存在しないと分かっているタイトなループでは循環GCを無効にし、その後再度有効にしてコレクションを強制実行します。
import gc
gc.disable()
try:
result = tight_loop_no_cycles()
finally:
gc.enable()
gc.collect()isenabledとisfinalized
gc.isenabled() でGCの状態を確認し、gc.is_finalized(obj) でオブジェクトのファイナライズが行われているかどうかを検出します。
import gc
print(gc.isenabled()) # True by default
obj = object()
print(gc.is_finalized(obj)) # False (still alive)メモリリークのパターン
Pythonでよくあるメモリリークの原因には、際限なく増大するグローバルキャッシュ、解除されていないイベントリスナー、__del__ を含む参照サイクル、参照を解放しないC拡張があります。
# Typical leaks:
global_cache = {} # grows forever — use lru_cache with maxsize
class EventBus:
listeners = []
# Listeners hold references to objects:
# @classmethod
# def on(cls, fn): cls.listeners.append(fn)
# Must call off() to detach確認問題
参照サイクルに含まれる __del__ を持つオブジェクトはどうなりますか?
まとめ
循環GCは、参照カウントでは処理できない参照サイクルを処理します。3つの世代を使用し、生存したオブジェクトを昇格させます。weakrefsを使用してサイクルを避けてください。サイクル内の __del__ を持つオブジェクトは gc.garbage に入ります。長時間実行するサーバーでは、起動後に gc.freeze() を使用してGCによる一時停止を減らします。
よくある質問
「ガベージコレクターと循環参照」レッスンは無料ですか?
はい。「ガベージコレクターと循環参照」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。
「ガベージコレクターと循環参照」で何を学びますか?
gc モジュールが参照サイクルを処理する方法を学びます。 ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Python Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「ガベージコレクターと循環参照」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このPython Academyレッスンでコードを書いて実行できますか?
はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。