Python以參考計數器為主,標記清除與分代回收為輔+快取機制來進行記憶體管理和垃圾回收
1.參考計數器
1.1 環狀雙向鏈表(refchain)
Python中創建的任何物件都會放在refchain中

eg:
name = 'ty' # 內部會創建一個結構體(上一個物件,下一個物件,型別,參考個數)
new = name # 此時'ty'結構體中的參考個數為2
age = 18 # 內部創建一個結構體(上一個物件,下一個物件,型別,參考個數,val = 18)
hobby = ['美女', '跑步'] # 內部創建一個結構體(上一個物件,下一個物件,型別,參考個數,元素個數,items = 元素)
1.1.1 c原始碼中如何體現每個物件中都有相同的值:PyObject結構體(4個值)
如何體現由多個元素組成的物件: PyObject結構體(4個值)+ob_size(元素個數)

1.2 型別封裝結構體

eg
data = https://www.cnblogs.com/tyyy/p/3.14
內部創建:
_ob_prev = refchain中上一個物件
_obj_next = refchain中下一個物件
_ob_refcnt = 1 # 參考計數器
_ob_type = float
_ob_fval = 3.14
1.3 參考計數器

v1 = 3.14
v2 = 999
v3 = (1, 2, 3)
當Python運行時,會根據資料型別的不同找到其對應的結構體,根據結構體中的欄位來進行創建相關的資料,然后將物件添加到refchain雙向鏈表中,
在原始碼中有兩個關鍵的結構體:PyObject(公共的值) & PyVarObject(多個元素組成時公共的值),
每個物件中的ob_refcnt就是參考計數器,值默認為1,當有其他變數時,參考物件的參考計數器就會發生變化(+1)
eg
參考:
a = 9999
b = a # 此時ob_refcnt = 2
洗掉參考:
a = 9999
b = a
del b # b變數洗掉;對應物件參考計數器-1
del a # a變數洗掉;對應物件參考計數器-1
當ob_refcnt為0時,意味著沒有人使用此物件了,這個物件就是垃圾,進行垃圾回收
問題:如何進行垃圾回收?
1.物件從refchain鏈表中移除,
2.將物件從記憶體中銷毀,記憶體歸還給作業系統,
1.4回圈參考&交叉感染
-僅僅用ob_refcnt就會出現這個問題

為了解決這個問題有引入了標記清除
2.標記清除
目的:解決參考計數器回圈參考的不足
實作:在Python的底層在維護一個鏈表,鏈表中專門存放那些可能存在回圈參考的物件(list/dict/set/tuple)

Python內部,某種情況下觸發,會去掃描可能存在回圈參考的鏈表中的每一個元素(元素的子元素也被掃描),檢查是否有回圈參考,如
有,讓雙方的參考計數器-1,若是0則垃圾回收,
問題:什么時候掃描?
可能存在回圈參考的鏈表掃描代價太大,每次掃描耗時太久
引入分代回收
3.分代回收

將可能存在回圈參考的物件維護成3個鏈表
0代:0代中物件個數達到700時掃描一次,
1代:0代掃描10次,則1代掃描一次,
2代:1代掃描10次,則2代掃描一次,
將不是垃圾的物件向上升代,否則剔除,
4.小結
在Python中維護了一個refchain的雙向環狀鏈表,這個鏈表中存盤程式創建的所有物件,每種型別的物件中都有一個ob_refcnt參考計數器的值,參考個數+1,-1,最后當參考計數器變為0時會進行垃圾回收(refchain中移除,物件銷毀),
但是,在Python中對于那些可以有多個元素組成的物件,可能會存在回圈參考的問題,為了解決這個問題Python又引入了標記清除和分代回收,在其內部維護4個鏈表,
refchain
2代,10次
1代,10次
0代,700個
在原始碼內部當達到各自的閾值時,就會觸發掃描鏈表進行標記清除的動作(有回圈則各自-1)
but,原始碼內部在上述的流程中提出了優化機制
5.Python快取
5.1池(int)
為了避免重復創建和銷毀一些常見物件,維護池,
# 啟動解釋器時,Python內部幫我們創建: -5,-4... ... ...257
v1 = 7 # 內部不會開辟記憶體,直接去池中獲取
v2 = 9 # 內部不會開辟記憶體,直接去池中獲取
v3 = 9 # 內部不會開辟記憶體,直接去池中獲取
5.2 free_list (float/list/tuple/dict)
當一個物件的參考計數器為0時,按理說應該回收,內部不會直接回收,而是將物件添加到free_list鏈表中當快取,以后再去創建物件時,不再重新開辟記憶體,而是直接使用free_list,
eg:
v1 = 3.14 # 開辟記憶體,內部存盤結構中定義那幾個值,并存到refchainh中
del v1 # refchain中移除,將物件添加到 free_list 中(80個),free_list滿了則銷毀,
v9 = 999.99 # 不會重新開辟記憶體,而是去free_list中獲取物件,物件內部資料初始化,再放到refchain中,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/140877.html
標籤:Python
