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