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无法回收这些资源,导致垃圾产生,内存泄露。

原文地址:https://www.cnblogs.com/ankier/p/3000760.html