通常情况下,python的gc 垃圾回收机制,有一套算法,可以用来回收循环引用的对象,避免内存泄露。
不过,有个例外的情况:显示重写了__del__方法。此时gc就无法释放资源,因为循环引用导致了引用计数器不可能为0。需要打破这种循环引用关系,才能释放资源。这就催生了招到一种,能去找出我们的程序代码中,存在的循环引用的关系。
gc中gc.garbage记录了所有不可回收的垃圾。gc.get_referents方法可以用来获得所有引用到该对象的资源。代码如下:
注意如下几点:
1. 所有的class应该继承与object或者,都有一个共同的基类object,这也是一个好的设计习惯。在该检测循环引用方法中,如果没有继承object,则发现会有死循环问题。而且检测的输出结果也不正确。()
2.为了保证class的print结果具有可参考性,可以重写其__str__()方法,输出一些有用信息。
代码:
importgcimportpprintimportQueueclassTeacher(object):def __init__(self):
self.name='dan dan'self.Stu=Nonedef __del__(self):print 'test'
def __str__(self):return '"class =%s, name = %s "'%(self.__class__.__name__, self.name)classStudent(object):def __init__(self):
self.name= 'Miss wang'self.Tea=Nonedef __del__(self):print 'des'
def __str__(self):return '"class =%s, name = %s "'%(self.__class__.__name__, self.name)classGraph(object):def __init__(self, name):
self.id= 1self.f= 2.0self.name=name
self.next=None
self.Text=Nonedefset_next(self, next):
self.next=nextdef __str__(self):return '"class =%s, name = %s "'%(self.__class__.__name__, self.name)def __del__(self):print 'destoried'
deftestTeacherStudents():
tea=Teacher()
stu=Student()#set
tea.Stu =stu
stu.Tea=teadeftestGraphs():#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)deffindAllCircleRefs():
gc.collect()for gb ingc.garbage:
seen=set()
to_process=Queue.Queue()#Start with an empty object chain and Graph three.
to_process.put( ([], gb) )#Look for cycles, building the object chain for each object we find
#in the queue so we can print the full cycle when we're done.
while notto_process.empty():
chain, next=to_process.get()
chain=chain[:]
chain.append(next)
seen.add(id(next))
allRefs=gc.get_referents(next)for r inallRefs:if not r or isinstance(r, basestring) or isinstance(r, int) or isinstance(r, type) or isinstance(r, float) orisinstance(r, tuple):#Ignore strings and classes
pass
elif id(r) inseen:print
print 'Found a cycle to %s:' %rfor i, link inenumerate(chain):print '%d:' %i, linkelse:
to_process.put( (chain, r) )
testTeacherStudents()
testGraphs()
findAllCircleRefs()
输出结果:
Found a cycle to "class =Teacher, name = dan dan":
0:"class =Teacher, name = dan dan"
1: {'Stu': <__main__.student object at>, 'name': 'dan dan'}2: "class =Student, name = Miss wang"
3: {'Tea': <__main__.teacher object at>, 'name': 'Miss wang'}
Found a cycle to"class =Student, name = Miss wang":
0:"class =Student, name = Miss wang"
1: {'Tea': <__main__.teacher object at>, 'name': 'Miss wang'}2: "class =Teacher, name = dan dan"
3: {'Stu': <__main__.student object at>, 'name': 'dan dan'}
Found a cycle to"class =Graph, name = one":
0:"class =Graph, name = one"
1: {'Text': None, 'next': <__main__.graph object at>, 'id': 1, 'name': 'one', 'f': 2.0}2: "class =Graph, name = two"
3: {'Text': None, 'next': <__main__.graph object at>, 'id': 1, 'name': 'two', 'f': 2.0}4: "class =Graph, name = three"
5: {'Text': None, 'next': <__main__.graph object at>, 'id': 1, 'name': 'three', 'f': 2.0}
Found a cycle to"class =Graph, name = two":
0:"class =Graph, name = two"
1: {'Text': None, 'next': <__main__.graph object at>, 'id': 1, 'name': 'two', 'f': 2.0}2: "class =Graph, name = three"
3: {'Text': None, 'next': <__main__.graph object at>, 'id': 1, 'name': 'three', 'f': 2.0}4: "class =Graph, name = one"
5: {'Text': None, 'next': <__main__.graph object at>, 'id': 1, 'name': 'one', 'f': 2.0}
Found a cycle to"class =Graph, name = three":
0:"class =Graph, name = three"
1: {'Text': None, 'next': <__main__.graph object at>, 'id': 1, 'name': 'three', 'f': 2.0}2: "class =Graph, name = one"
3: {'Text': None, 'next': <__main__.graph object at>, 'id': 1, 'name': 'one', 'f': 2.0}4: "class =Graph, name = two"
5: {'Text': None, 'next': <__main__.graph object at>, 'id': 1, 'name': 'two', 'f': 2.0}
由上面的输出结果,可以看到Teach类和student类产生了循环引用。
graph的one,two和three之间产生了一个引用环。
重写了__del__方法及这些循环引用,导致了循环引用计数器无法归零,gc无法回收这些资源,导致垃圾产生,内存泄露。