在Python中,垃圾回收器经过引用计数来回收垃圾对象。但某些环状数据结果(树、图、双向链表等),存在对象间的循环引用,好比树的父节点引用子节点,子节点同时也引用父节点。若是同时del掉引用父子节点,两个对象不能被当即回收。html
要求:解决此类的内存管理问题。node
解决方案:ide
使用标准库weakref.ref
类,它能够建立一种能访问对象但不增长引用计数的对象。函数
class weakref.ref(object[, callback])
返回对对象的弱引用。若是原始对象仍然存活,则能够经过调用引用对象来检索原始对象;若是引用的原始对象再也不存在,则调用引用对象将获得None。3d
Python 的弱引用模块weakref容许垃圾回收器(garbage collector)在一个对象没有强引用的时候回收内存。code
对一个对象的弱引用,相对于一般的引用来讲,若是一个对象有一个常规的引用,它是不会被垃圾回收器销毁的,可是若是一个对象只剩下一个弱引用,那么它可能被垃圾回收器收回。htm
要保持追踪内存中的对象,Python使用了引用计数这一简单的技术,当一个对象的引用计数为0时该对象会被释放,此时会调用析构函数。若是要显式的调用析构函数,能够使用del关键字。对象
sys.getrefcount(a)
能够查看a对象的引用计数,可是比正常计数大1,由于调用函数的时候传入a,这会让a的引用计数+1。blog
>>> class A: def __del__(self): #析构函数 print('in __del__') >>> a = A()>>> import sys>>> sys.getrefcount(a) #a的引用计数为12>>> a2 = a>>> sys.getrefcount(a) #a的引用计数为23>>> a2 = None>>> a = 1in __del__ #此时直接调用析构函数,说明引用计数已经为0
import timeclass Node: def __init__(self, data): self.data = data self.left = None self.right = None def add_right(self, node): self.right = node node.left = self def __str__(self): #打印node return 'None:' % self.data def __del__(self): #查看释放过程 print('in __del__: delete %s' % self)def create_linklist(n): head = current = Node(1) for i in range(2, n+1): node = Node(i) current.add_right(node) current = node return head head = create_linklist(1000)head = None #令head引用计数减1for _ in range(1000): #模拟程序运行 time.sleep(1) print('run...')input('wait...')
此时在释放双向链表的head(头节点)时,内存一直没法释放,由于头节点的引用计数不为0,这就致使后面的节点的引用计数也不为0。图片
改进:使用弱引用,经过标准库weakref.ref
类,建立一种能访问对象但不增长引用计数的对象。
import timeclass Node: def __init__(self, data): self.data = data self._left = None self.right = None def add_right(self, node): self.right = node node._left = weakref.ref(self) #建立弱引用 @property def left(self): return self._left def __str__(self): return 'None:' % self.data def __del__(self): print('in __del__: delete %s' % self)def create_linklist(n): head = current = Node(1) for i in range(2, n+1): node = Node(i) current.add_right(node) current = node return head head = create_linklist(1000)head = None #令head引用计数减1for _ in range(1000): #模拟程序运行 time.sleep(1) print('run...')input('wait...')
此时运行程序,能够看到析构函数的调用,即内存当即释放。