objgraphを使用して、次のようなオブジェクトの束を見つけました。
Python のガベージ コレクターはこのようなサイクルを処理しますか、それともリークしますか?
ループの少し広いビュー:
Python の標準参照カウント メカニズムはサイクルを解放できないため、この例の構造はリークします。
ただし、補助的なガベージ コレクション機能はデフォルトで有効になっており、そのコンポーネントのいずれも外部からアクセスできなくなり、メソッドがない__del__()
場合は、その構造を解放できるはずです。
その場合、ガベージ コレクターは__del__()
これらのメソッドを安全に実行する順序を判断できないため、それらを解放しません。
フレデリックの答えを少し拡張するために、ドキュメントの「参照カウント」セクションでは、補助サイクルの検出についてうまく説明しています。
物事を説明することは、理解していることを確認する良い方法だと思うので、ここにいくつかの例を示します...これらの2つのクラスでは:
class WithDel(object):
def __del__(self):
print "deleting %s object at %s" % (self.__class__.__name__, id(self))
class NoDel(object):
pass
オブジェクトを作成して参照を失うと、参照カウントのおかげでメソッドa
がトリガーされます。__del__
>>> a = WithDel()
>>> a = None # leaving the WithDel object with no references
deleting WithDel object at 4299615184
メソッドを使用せず に 2 つのオブジェクト間で参照ループを作成して__del__
も、今回はサイクル検出のおかげで、すべてが漏れることはありません。まず、ガベージ コレクションのデバッグ出力を有効にします。
>>> import gc
>>> gc.set_debug(gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS)
次に、2 つのオブジェクト間で参照ループを作成します。
>>> a = NoDel(); b = NoDel()
>>> a.other = b; b.other = a # cyclical reference
>>> a = None; b = None # Leave only the reference-cycle
>>> gc.collect()
gc: collectable <NoDel 0x10046ed50>
gc: collectable <NoDel 0x10046ed90>
gc: collectable <dict 0x100376c20>
gc: collectable <dict 0x100376b00>
4
>>> gc.garbage
[]
(これdict
はオブジェクトの内部__dict__
属性からのものです)
サイクル内のオブジェクトの 1 つでもメソッドが含まれるまで、すべて問題ありません。__del__
>>> a = NoDel(); b = WithDel()
>>> a.other = b; b.other = a
>>> a = None; b = None
>>> gc.collect()
gc: uncollectable <WithDel 0x10046edd0>
gc: uncollectable <dict 0x100376b00>
gc: uncollectable <NoDel 0x10046ed90>
gc: uncollectable <dict 0x100376c20>
4
>>> gc.garbage
[<__main__.WithDel object at 0x10046edd0>]
ポールが述べたように、ループは次のように壊すことができますweakref
:
>>> import weakref
>>> a = NoDel(); b = WithDel()
>>> a.other = weakref.ref(b)
>>> b.other = a # could also be a weakref
次に、オブジェクトb
への参照WithDel
が失われると、サイクルにもかかわらず削除されます。
>>> b = None
deleting WithDel object at 4299656848
>>> a.other
<weakref at 0x10045b9f0; dead>
ああ、objgraphは、このような問題のある__del__
メソッドを有益に示していたでしょう
Python の GC は、すべてのライブ オブジェクトをトラバースして、外部参照なしで参照サイクルを見つけて排除するように設計されています。
gc.get_objectsを実行しgc.collect()
てから印刷することで、それが起こっていることを確認できます。gc.garbage
親ポインターにweakrefsを使用すると、GCは正常に実行されます。