17.6 gc:垃圾回收器
gc提供了Python的底层内存管理机制,即自动垃圾回收器。这个模块包括一些函数,可以控制回收器如何操作,以及如何检查系统已知的对象,这些对象可能在等待收集,也可能已陷入引用环而无法释放。
17.6.1 跟踪引用
利用gc,可以使用对象之间来回的引用来查找复杂数据结构中的环。如果已知一个数据结构中存在环,那么可以使用定制代码来检查它的属性。如果环在未知代码中,则可以使用get_referents()和get_referrers)_函数建立同样的调试工具。
例如,get_referents()显示了输入参数引用的对象。
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)
# 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)
print()
print('three refers to:')
for r in gc.get_referents(three):
pprint.pprint(r)
在这里,Graph实例three包含其实例字典(在__dict__属性中)以及其类的引用。
下一个例子使用一个Queue来对所有对象引用完成一个广度优先遍历,以查找环。插入到队列中的元素为元组,其中包含目前为止的引用链和下一个要检查的对象。检查从three开始,要查看它引用的所有对象。跳过类意味着不检查它的方法、模块和其他组件。
import gc
import pprint
import queue
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)
# 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)
print()
seen = set()
to_process = queue.Queue()
# Start with an empty object chain and Graph three.
to_process.put(([],three))
# Look for cycles,building the object chain for each object
# found in the queue so the full cycle can be printed at the
# end.
while not to_process.empty():
chain,next = to_process.get()
chain = chain[:]
chain.append(next)
print('Examining:',repr(next))
seen.add(id(next))
for r in gc.get_referents(next):
if isinstance(r,str) or isinstance(r,type):
# Ignore serings and classes.
pass
elif id(r) in seen:
print()
print('Found a cycle to {}:'.format(r))
for i,link in enumerate(chain):
print(' {}: '.format(i),end=' ')
pprint.pprint(link)
else:
to_process.put((chain,r))
通过监视已处理的对象,可以狠容易地发现节点中的环。所以这些对象的引用不会被收集,它们的id()值会缓存在一个集合中。环中找到的字典对象是Graph实例的__dict__值,其包括它们的实例属性。