python的垃圾回收機制
1. 垃圾回收機制的算法分類
python垃圾回收算法通常有三類:引用計數,標記清除和分代回收,主要以引用計數為主,標記清除和分代回收為輔
2. 對象的存儲方式——refchain環狀雙向鏈表
在Python中創建的任何對象都會放在refchain的雙向鏈表中

C語言中結構體的定義代碼,即refchain中存在四個共性的四個屬性值:prev、next、refcnt(引用計數器)、type(類型)
但是如列表、元組、字典等類型時,增加size屬性,即元素個數

不同的類型的不同屬性

3. 引用計數
1. 引用計數的原理
1. 每個對象在創建時都會有一個引用計數器來記錄引用次數,即ob_refcnt屬性,默認值為1
2. 當對象被引用時,引用計數器會進行+1
3. 當對象不被引用或者銷毀時,引用計數器會進行-1
4. 當引用計數器為0時,對象被回收,即在refchain鏈表中刪除,釋放系統內存
例:
import sys class A: def __init__(self): print('對象被創建') def __del__(self): print('對象被銷毀') # sys.getrefcount方法用來獲取指定對象計數器的值 print(sys.getrefcount(A())) # 創建A()對象,計數器值為 1 a = A() print(sys.getrefcount(a)) # 對象A()創建并被變量a引用,計數器值為 2 b = a print(sys.getrefcount(b)) # 變量b等于變量a,即指向a的地址A(), 計數器為 3 a = 1 print(sys.getrefcount(b)) # 變量a指向其他對象,A()對象的引用數 -1,變為 2 b = 1 # 變量b指向其他對象,A()對象的引用數 1,同時A()對象被銷毀,計數器 -1,變為 0,被回收 print('程序結束')
2. 引用計數的優缺點
1. 優點
1. 簡單高效
2. 時效性高,只要當對象的計數器為0時,立即進行回收
2. 缺點
1. 維護計數器消耗大量的資源
2. 當出現字典,列表對對象的循環套用引用時,會造成對象之間互相引用,引發內存泄漏
4. 標記清除
1. 循環引用造成的后果
# 循環引用例子 class L1(dict): def __del__(self): print('對象1被銷毀') class L2(dict): def __del__(self): print('對象2被銷毀') l1 = L1() # 對象L1創建的時候計數器為 1,,被l1引用,此時計數器為 2 l2 = L2() # 對象L2創建的時候計數器為 1,,被l1引用,此時計數器為 2 print(sys.getrefcount(l1)) # 2 print(sys.getrefcount(l2)) # 2 l1['a'] = l2 # l1的鍵a引用l2,此時L2對象的引用為2,所以計數器為 3 l2['b'] = l1 # l2的鍵b引用l1,此時L1對象的引用為2,所以計數器為 3 print(sys.getrefcount(l1)) # 3 print(sys.getrefcount(l2)) # 3 # 字典被刪除,但是字典中的值(即L1,L2對象)互相引用著,理論上用不著,但是無法被引用計數算法回收 del l1 # 刪除l1,但是這里對象仍在被引用,計數器仍為 2,不會被回收 del l2 # 刪除l2,但是這里對象仍在被引用,計數器仍為 2,不會被回收 print('程序結束') # 對象在整個程序結束才被回收,而不是del的時候被回收
圖解:

2. 標記清除的實現原理
1. 標記:將所有的對象看做是一個點,并將對象的引用關系構造圖結構,從根節點出發遍歷所有的點,能訪問到的點標記為“可達對象”
2. 清除:遍歷所有對象,若沒有被標記為“可達對象“則進行回收
class A: def __init__(self): pass def __del__(self): print('對象被銷毀') def func(): a = A() # a引用對象A b = A() # b引用對象A c = A() # c引用對象A d = A() # d引用對象A e = c # e引用c f = c # f引用c # a,b對象的屬性互相引用 a.obj = b b.obj = a # 不返回a,b,表明兩個變量引用被刪除了 return [d,e,f] g = func() print('程序結束,回收垃圾')

5. 分代回收
分代回收是建立在標記清除的基礎上,掃描對象需要定義一個觸發點,如果時時刻刻進行掃描,那么增加了程序的運行時間,通常將循環引用的對象分為三代:0代、1代和2代
0代:對象剛剛被創建時分配到0代鏈表
1代:經過一輪GC掃描存貨下來的,放置1代鏈表中
2代:再次經過掃描存活下來的對象,分至2代鏈表中

觸發GC掃描機制
浙公網安備 33010602011771號