17.6.3 查找无法回收的对象引用
要在垃圾列表中查找哪个对象包含另一个对象的引用,这比查看一个对象引用了什么要麻烦一些。因为查找引用的代码本身也需要包含引用,所以需要忽略一些包含引用的对象(引用者)。下面这个例子创建了一个图环,然后处理Graph实例,并删除"父"节点中的引用。
import gc
import pprint
class Graph:
def __init__(self,name):
self.name = name
self.next = None
def set_next(self,next):
print('Linking nodes {}.next = {}'.format(self,next))
self.next = next
def __repr__(self):
return '{}({})'.format(
self.__class__.__name__,self.name)
def __del__(self):
print('{}.__del__()'.format(self))
# Construct a graph cycle.
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)
# Collecting now keeps the objects as uncollectable,
# but not garbage.
print()
print('Collecting...')
n = gc.collect()
print('Unreachable objects:',n)
print('Remaining Garbage:',end=' ')
pprint.pprint(gc.garbage)
# Ignore references from local variables in this module,global
# variables,and from the garbage collector's bookkeeping.
REFERRERS_TO_IGNORE = [locals(),globals(),gc.garbage]
def find_referring_graphs(obj):
print('Looking for references to {!r}'.format(obj))
referrers = (r for r in gc.get_referrers(obj)
if r not in REFERRERS_TO_IGNORE)
for ref in referrers:
if isinstance(ref,Graph):
# A graph node
yield ref
elif isinstance(ref,dict):
# An instance or other namespace dictionary
for parent in find_referring_graphs(ref):
yield parent
# Look for objects that refer to the objects i nthe graph.
print()
print('Clearing referrers:')
for obj in [one,two,three]:
for ref in find_referring_graphs(obj):
print('Found referrer:',ref)
ref.set_next(None)
del ref # Remove reference so the node can be deleted.
del obj # Remove reference so the node can be deleted.
# Clear references held by gc.garbage.
print()
print('Clearing gc.garbage:')
del gc.garbage[:]
# Everything should have been freed this time.
print()
print('Collecting...')
n = gc.collect()
print('Unreachable objects:',n)
print('Remaining Garbage:',end=' ')
pprint.pprint(gc.garbage)
如果环是已知的,那么这种逻辑有些“大材小用”,不过对于数据中不可解释的环,使用get_referrers()可以发现未曾预料到的关系。