python的垃圾回收機(jī)制:
主要是引用計(jì)數(shù)為主,分代回收為輔。
一、引用計(jì)數(shù)
引用計(jì)數(shù)的原理:
// object.h
struct _object {
? ? ????Py_ssize_t ob_refcnt;? # 引用計(jì)數(shù)值? ??
????????struct PyTypeObject *ob_type;
} PyObject
每個(gè)對(duì)象維護(hù)一個(gè)ob_ref。用來(lái)記錄當(dāng)前對(duì)象被引用的次數(shù),也就是用來(lái)追蹤到底有多少引用指向了這個(gè)對(duì)象。
當(dāng)發(fā)生以下四種情況,該對(duì)象的引用計(jì)數(shù)+1
1、對(duì)象被創(chuàng)建? ? a = 10
2、對(duì)象被引用? ? b = a
3、對(duì)象被作為參數(shù),傳到函數(shù)中? ? func(a)
當(dāng)發(fā)生以下四種情況,該對(duì)象的引用計(jì)數(shù)-1
1、當(dāng)該對(duì)象的別名被顯示銷毀時(shí)? ? ?del a
2、當(dāng)該對(duì)象的引別名被賦予新的對(duì)象? ? a = 26
3、一個(gè)對(duì)象離開(kāi)它的作用域,例如 func函數(shù)執(zhí)行完畢時(shí),函數(shù)里面的局部變量的引用計(jì)數(shù)器就會(huì)減一(全部變量不會(huì))
4、將該元素從容器中刪除時(shí),或者容器被銷毀時(shí)。
當(dāng)指向該對(duì)象的內(nèi)存的引用計(jì)數(shù)器為0的時(shí)候,該內(nèi)存將會(huì)被Python虛擬機(jī)立即銷毀
引用計(jì)數(shù)法的優(yōu)點(diǎn):
1.高效
2.實(shí)時(shí)性:一旦沒(méi)有引用,內(nèi)存就直接釋放了。不用像其他機(jī)制等到特定時(shí)機(jī)。實(shí)時(shí)性還帶來(lái)一個(gè)好處:處理回收內(nèi)存的時(shí)間分?jǐn)偟搅似綍r(shí)。
3.對(duì)象有確定的生命周期
4.易于實(shí)現(xiàn)
原始的引用計(jì)數(shù)法也有明顯的缺點(diǎn):
1.無(wú)法解決循環(huán)引用的問(wèn)題。A和B相互引用而再?zèng)]有外部引用A與B中的任何一個(gè),它們的引用計(jì)數(shù)都為1,但顯然應(yīng)該被回收。
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
為了解決這兩個(gè)致命弱點(diǎn),Python又引入了以下兩種GC機(jī)制。
二、標(biāo)記-清除
Python的引用計(jì)數(shù)算法不能夠處理互相指向自己的對(duì)象。Python的GC不能夠處理未使用的對(duì)象因?yàn)閼?yīng)用計(jì)數(shù)值不會(huì)到零。
這就是為什么Python要引入Generational GC算法的原因!
『標(biāo)記清除(Mark—Sweep)』算法是一種基于追蹤回收(tracing GC)技術(shù)實(shí)現(xiàn)的垃圾回收算法。它分為兩個(gè)階段:第一階段是標(biāo)記階段,GC會(huì)把所有的『活動(dòng)對(duì)象』打上標(biāo)記,第二階段是把那些沒(méi)有標(biāo)記的對(duì)象『非活動(dòng)對(duì)象』進(jìn)行回收。那么GC又是如何判斷哪些是活動(dòng)對(duì)象哪些是非活動(dòng)對(duì)象的呢?
對(duì)象之間通過(guò)引用(指針)連在一起,構(gòu)成一個(gè)有向圖的節(jié)點(diǎn),而引用關(guān)系構(gòu)成這個(gè)有向圖的邊。從根對(duì)象(root object)出發(fā),沿著有向邊遍歷對(duì)象,可達(dá)的(reachable)對(duì)象標(biāo)記為活動(dòng)對(duì)象,不可達(dá)的對(duì)象就是要被清除的非活動(dòng)對(duì)象。根對(duì)象就是全局變量、調(diào)用棧、寄存器。

在上圖中,我們把小黑點(diǎn)作為全局變量,也就是root object,對(duì)象1可直達(dá),那么它將被標(biāo)記,對(duì)象2、3可間接到達(dá)也會(huì)被標(biāo)記,而4和5不可達(dá),那么1、2、3就是活動(dòng)對(duì)象,4和5是非活動(dòng)對(duì)象會(huì)被GC回收。
“標(biāo)記-清除”法是為了解決循環(huán)引用問(wèn)題。
因?yàn)檠h(huán)引用的原因,并且因?yàn)槟愕某绦蚴褂昧艘恍┍绕渌麑?duì)象存在時(shí)間更長(zhǎng)的對(duì)象,從而被分配對(duì)象的計(jì)數(shù)值與被釋放對(duì)象的計(jì)數(shù)值之間的差異在逐漸增長(zhǎng)。一旦這個(gè)差異累計(jì)超過(guò)某個(gè)閾值,則Python的收集機(jī)制就啟動(dòng)了,并且觸發(fā)上邊所說(shuō)到的零代算法,釋放“浮動(dòng)的垃圾”,并且將剩下的對(duì)象移動(dòng)到一代列表。
一旦被分配計(jì)數(shù)值與被釋放計(jì)數(shù)值累計(jì)到達(dá)一定閾值,Python會(huì)將剩下的活躍對(duì)象移動(dòng)到二代列表。
Python處理零代最為頻繁,其次是一代然后才是二代。
然后根據(jù)引用計(jì)數(shù)副本值是否為0將集合內(nèi)的對(duì)象分成兩類,reachable和unreachable,其中unreachable是可以被回收的對(duì)象:
三、分代回收
先給出gc的邏輯
分配內(nèi)存
->發(fā)現(xiàn)超過(guò)閾值了
->觸發(fā)垃圾回收
->將所有可收集對(duì)象鏈接放到一起
->遍歷,計(jì)算有效引用計(jì)數(shù)
->分成有效引用計(jì)數(shù)=0和有效引用計(jì)數(shù) > 0兩個(gè)集合
->大于0的,放入到更老一代
->=0的執(zhí)行回收
->回收遍歷容器內(nèi)的各個(gè)元素,減掉對(duì)應(yīng)元素引用計(jì)數(shù)(砍掉循環(huán)引用)
->執(zhí)行-1的邏輯,若發(fā)現(xiàn)對(duì)象引用計(jì)數(shù)=0,觸發(fā)內(nèi)存回收
->python底層內(nèi)存管理機(jī)制回收內(nèi)存
Python中, 引入了分代收集, 總共三個(gè)”代”. Python 中, 一個(gè)代就是一個(gè)鏈表, 所有屬于同一”代”的內(nèi)存塊都鏈接在同一個(gè)鏈表中
??用來(lái)表示“代”的結(jié)構(gòu)體是gc_generation, 包括了當(dāng)前代鏈表表頭、對(duì)象數(shù)量上限、當(dāng)前對(duì)象數(shù)量:
// gcmodule.c
struct gc_generation {
????????PyGC_Head head;
? ? ????int threshold; /* collection threshold */
? ? ????int count; /* count of allocations or collections of younger???? generations */
};
新生成的對(duì)象會(huì)被加入第0代,前面_PyObject_GC_Malloc中省略的部分就是Python GC觸發(fā)的時(shí)機(jī)。每新生成一個(gè)對(duì)象都會(huì)檢查第0代有沒(méi)有滿,如果滿了就開(kāi)始著手進(jìn)行垃圾回收.
分代回收總結(jié):
??分代回收是一種以空間換時(shí)間的操作方式,Python將內(nèi)存根據(jù)對(duì)象的存活時(shí)間劃分為不同的集合,每個(gè)集合稱為一個(gè)代,Python將內(nèi)存分為了3“代”,分別為年輕代(第0代)、中年代(第1代)、老年代(第2代),他們對(duì)應(yīng)的是3個(gè)鏈表,它們的垃圾收集頻率與對(duì)象的存活時(shí)間的增大而減小。新創(chuàng)建的對(duì)象都會(huì)分配在年輕代,年輕代鏈表的總數(shù)達(dá)到上限時(shí),Python垃圾收集機(jī)制就會(huì)被觸發(fā),把那些可以被回收的對(duì)象回收掉,而那些不會(huì)回收的對(duì)象就會(huì)被移到中年代去,依此類推,老年代中的對(duì)象是存活時(shí)間最久的對(duì)象,甚至是存活于整個(gè)系統(tǒng)的生命周期內(nèi)。同時(shí),分代回收是建立在標(biāo)記清除技術(shù)基礎(chǔ)之上。分代回收同樣作為Python的輔助垃圾收集技術(shù)處理那些容器對(duì)象.
參考文獻(xiàn)
原文鏈接:https://blog.csdn.net/xiongchengluo1129/article/details/80462651