實際案例:
在python中,垃圾回收器通過引用計數來回收垃圾對象,但某些環狀數據結構(樹,圖...),存在對象間的循環引用,比如樹的父節點引用子節點,子節點也同時引用父節點。此時同時del掉引用父子節點,兩個對象不能被立即回收。
如何解決此類的內存管理問題?
解決方案:
使用標准庫weakref,它可以創建一種能訪問對象但不增加引用計數對象。
(1)垃圾回收--引用計數介紹及弱引用weakref簡單使用
import sys
class A(object):
def __del__(self):
# 定義析構函數,在實例被回收時會被調用
print(' in __del__')
a = A()
# 查看對象的引用計數,結果為2,因為在調用這個函數時也傳入一次a
print(sys.getrefcount(a))
# 減去臨時變量的1次引用
print(sys.getrefcount(a) - 1)
# 將a賦值給a2引用計數會加1
a2 = a
print(sys.getrefcount(a) - 1)
# del會減去1次引用計數,當引用計數為0時,對象會被回收
del a2
# 改變a的指向,去引用數字5,此時不再引用A(),
# 引用計數為0對象被回收觸發析構函數。
a = 5
print(a)
a = A()
print(sys.getrefcount(a) - 1)
# 導入弱引用
import weakref
# 創建對象的弱引用
a_wref = weakref.ref(a)
# 它就跟函數一樣進行調用,此時就得到了一個A()的引用
a2 = a_wref()
# 可以看到同時引用A()這個對象
print(a is a2)
del a
# A()引用計數變為0,被回收
del a2
# 弱引用在對象存在時就返回對象引用,對象不存在就返回None
print(a_wref() is None)
(2)舉例說明循環引用問題及解決
class Data(object): # 節點數據類
def __init__(self, value, owner):
# self.owner = owner
self.owner = weakref.ref(owner)
self.value = value
def __str__(self):
# return "%s's data, value is %s" % (self.owner, self.value)
return "%s's data, value is %s" % (self.owner(), self.value)
def __del__(self):
print('in __del--')
class Node(object): # 節點類
def __init__(self, value):
# 循環引用
self.data = Data(value, self)
def __del__(self):
print('in Node.__del__')
node = Node(100)
# 循環引用不能被立即回升,在將來未知點被回收
del node
# 導入gc模塊強制回收
import gc
# 使用強制回收這種方式,也不能立即回收掉,
gc.collect()
'''
這就是循環引用的內存管理問題,為了解決這類問題
可以使用標准庫中的弱引用,弱引用就是可以訪問一個對象
但不增加它的引用計數。
'''
# 引用weakref弱引用修改owner為弱引用,如上代碼改動。
'''
讓數據的owner是對node弱引用,這樣也就不會增加node的引用計數
從此消除了循環引用的問題,在刪除node的的時候Data和Node對象都被回收掉。
'''
node = Node(100)
del node
# 刪除node直接觸發兩個實例的析構函數,運行結果:
in Node.__del__
in __del--