在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...')
此時運行程序,能夠看到析構函數的調用,即內存當即釋放。