因應上一篇提到的問題,Cpython還有兩種回收機制:標記與清除演算法與分代機制。
標記與清除演算法
首先,Cpython會創建一個紀錄可能需要回收物件的linked list,當物件被創造時,
Cpython會判斷該物件的類型是否開啟GC功能,假如有GC功能,則放入該linked list。
PyTypeObject PyList_Type = {
...
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_LIST_SUBCLASS |
_Py_TPFLAGS_MATCH_SELF | Py_TPFLAGS_SEQUENCE, /* tp_flags */
...
};
我們可以發現list有Py_TPFLAGS_HAVE_GC,所以當我們創建list類型的物件,此物件就會
被放入list。
另外,Cpython會在該物件上加上PyGC_Head,PyGC_Head包含兩個指標:
prev指向前一個須追蹤物件,next指向下一個須追蹤物件
透過兩個指標,Cpython創造一個linked list追蹤可能需要回收的物件。
回收機制標記了可能需回收的物件,之後,它將物件區分為兩類:
可達(reachable)與不可達(unreachable)
可達是指有變數引用或有可達物件引用;不可達則無。
區分可達與不可達後,清除就是回收不可達物件。
分代機制
我們有了標記與清除演算法,我們可以設定每過一段時間就清除一次,但,每清除一次就
必須遍歷整個linked list,linked list裡面可能有一些長時間使用的東西,每次遍歷都
會掃到很多不需要清理的物件,所以,Cpython設計了分代機制。
分代機制就是將物件列表分成三代:
第零代:新物件被創造就放在這裡
第一代:第零代被清理後,活下來的物件移到這裡
第二代:第一代被清理後,活下來的物件移到這裡
再來討論何時回收這三代的物件
head前面我們提過了,每代還有threshold跟count
threshold是每一代的閥值,count每代的意義不同,在第零代,count代表第零代列表的
物件數量,每次創建可能需回收物件,count += 1。
第一代count代表第零代的垃圾回收次數,第二代的Count代表第一代的垃圾回收次數
當count > threshold,該代就會執行垃圾回收。
每代的可能需要遍歷的最大數量如下:
第零代:generation[0].threshold
第一代:generation[0].threshold * generation[0].threshold(第零代全部存活)
第二代:無上限
所以,第二代可能囤積大量物件,所以,為了減少回收第二代的次數
Cpython對第二代回收加了個條件:
當第二代中等待第一次完全回收的物件數量/上次完全回收存活下來的物件數量 > 1/4
第二代才會執行垃圾回收
GC linked list
關於此linked list如何新增與刪除新物件的問題
其實它就跟一般linked list差不多 不贅述
參考資料:
https://reurl.cc/jQMZd1