36. 在環狀數據結構中管理內存

在Python中,垃圾回收器經過引用計數來回收垃圾對象。但某些環狀數據結果(樹、圖、雙向鏈表等),存在對象間的循環引用,好比樹的父節點引用子節點,子節點同時也引用父節點。若是同時del掉引用父子節點,兩個對象不能被當即回收。html

要求:解決此類的內存管理問題。node

解決方案:ide

使用標準庫weakref.ref類,它能夠建立一種能訪問對象但不增長引用計數的對象。函數


  • 對於wekref模塊:
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...')

此時運行程序,能夠看到析構函數的調用,即內存當即釋放。

相關文章
相關標籤/搜索