Python 的垃圾回收回收机制(源码)
python內存管理及垃圾回收
1. 引用計數器
1.1 環狀雙向連表 refchain
在python程序中創建的任何對象都會放在refchain鏈表中,并且可以通過這個對象訪問到上一個和下一個對象。
name = '張三' age = 18 hobby = ['美女','吃飯'] 內部會建立一些數據 -打包 C語言叫做結構體-> 【上一個對象、下一個對象、類型、引用個數】 name = "張三" # 創建一個對象開辟內存空間 new = name # new指向指向"張三"這塊內存,不會重新分配內存,引用計數+1內部會建立一些數據 【上一個對象、下一個對象、類型、引用個數、val=18】 age = 18 # int會添加具體值內部會建立一些數據 【上一個對象、下一個對象、類型、引用個數、items=元素、元素個數】 hobby = ['美女','吃飯'] # 列表會添加元素和元素個數C語言源碼
- 每個對象都有相同的值:PyObject 結構體 (4個值)。
- 有多個元素組成的對象:PyObject 結構體 (4個值) + ob_size 。
源碼解析,包含:
- 2個結構體
- PyObject,此結構體中包含3個元素。
- _PyObject_HEAD_EXTRA,用于構造雙向鏈表。
- ob_refcnt,引用計數器。
- *ob_type,數據類型。
- PyVarObject,次結構體中包含4個元素(ob_base中包含3個元素)
- ob_base,PyObject結構體對象,即:包含PyObject結構體中的三個元素。
- ob_size,內部元素個數。
- PyObject,此結構體中包含3個元素。
- 3個宏定義
- PyObject_HEAD,代指PyObject結構體。
- PyVarObject_HEAD,代指PyVarObject對象。
- _PyObject_HEAD_EXTRA,代指前后指針,用于構造雙向隊列。
1.2 不同類型封裝的結構體
# float類型 data = 3.14內部會創建:_ob_next = refchain 中的上一個對象_ob_prev = refchain 中的下一個對象 ob_refcnt = 1 ob_type = floatob_fval = 3.14C源碼:
typedef struct {PyObject_HEADdouble ob_fval; } PyFloatObject;1.3 引用計數器
v1 = 3.14 v2 = 999 v3 = [1,2,3]當python程序運行時,會根據數據類型的不同找到其對應的結構體,根據結構體中的字段進行創建相關的數據,然后將對象添加到refchain雙向鏈表中。
在C源碼中有兩個關鍵的結構體:PyObject、PyVarObject。
每個對象中有ob_refcnt 就是引用計數器,默認值為1,當有其他變量引用對象時,引用計數器就會發生變化。
-
引用
a = 999 b = a # 999 這個對象的引用計數器+1,為2 -
刪除引用
a = 999 b = a del b # b變量刪除,b對應的對象的引用計數器 -1 del a # a變量刪除,a對應的對象的引用計數器 -1 (a、b對應同一個對象)""" 當一個對象的引用計數器為0時,意味著沒有人可以使用這個對象了,這個對象就是垃圾,需要被回收 回收:1.對象從refchain鏈表中移除2.將對象銷毀,內存歸還系統"""1.5 循環引用
2. 標記清除
目的:為了解決引用計數器循環引用的不足。
實現:在python的底層 再維護一個鏈表,鏈表中專門放那些可能存在循環引用的對象 (list/tuple/dict/set)。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-V9eqdYfm-1605967623655)(E:\07-notes\picture\57_解決循環引用圖.png)]
在python內部 某種情況下 觸發,會去掃描 可能存在循環引用的鏈表中的每一個元素,檢查是否有循環引用,如果有則雙方的引用計數器 -1,如果是 0 則垃圾回收。
問題:
- 什么時候掃描?
- 可能存在循環引用的鏈表掃描代價較大,每次掃描耗時久。
3. 分代回收
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-K55asNPj-1605967623657)(E:\07-notes\picture\58_分待回收圖
.png)]
將可能存在循環引用的對象維護成 3個鏈表:
- 0代:0代中對象個數達到700個掃描一次;
- 1代:0代掃描 10 次,則 1代掃描一次;
- 2代:1代掃描10次,則 2 代掃描一次。
4. 小結
在python中維護了一個refchain的雙向環狀鏈表,這個鏈表中存儲程序中創建的所有對象,每種類型的對象都有一個 ob_refcnt 的引用計數器的值,對象被引用,則計數器的值 +1,引用被刪除則計數器的值 -1,最后引用計數的值為 0 時,會進行垃圾回收(對象銷毀、從refchain中移除),
但是,在python中對于那些可以有多個元素組成的對象可能會存在循環引用問題,為了解決這個問題python引入了 標記清除 ,在其內部再維護了一個鏈表,專門放那些可能存在循環引用的對象 (list/tuple/dict/set), 某種情況下 觸發,會去掃描 可能存在循環引用的鏈表中的每一個元素,檢查是否有循環引用,如果有則雙方的引用計數器 -1,如果是 0 則垃圾回收,
然而,又有一個新的問題產生,就是什么時候掃描?可能存在循環引用的鏈表掃描代價較大,每次掃描耗時久,所以又引入了 分代回收 ,將可能存在循環引用對象維護成 3 個鏈表,分別是 0代,1代,2代,所有可能存在循環引用的對象都存儲在 0代鏈表,當對象個數達到700個的時候掃描一次,是垃圾則回收,不是則移代 1代,依次類推,0代掃描10次,1代掃描一次,1代掃描10次,2代掃描1次。
e=5>分代回收 ,將可能存在循環引用對象維護成 3 個鏈表,分別是 0代,1代,2代,所有可能存在循環引用的對象都存儲在 0代鏈表,當對象個數達到700個的時候掃描一次,是垃圾則回收,不是則移代 1代,依次類推,0代掃描10次,1代掃描一次,1代掃描10次,2代掃描1次。
總結
以上是生活随笔為你收集整理的Python 的垃圾回收回收机制(源码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win32中如何判断多个键同时按下
- 下一篇: python解析xml+得到pascal