(2018-04-08.Python從Zero到One)一、python高級(jí)編程__1.3.2垃圾回收(二)

上一篇文章為:→1.3.1import垃圾回收

垃圾回收(二)

1. Garbage collection(GC垃圾回收)

現(xiàn)在的高級(jí)語言如java,c#等升略,都采用了垃圾收集機(jī)制微王,而不再是c,c++里用戶自己管理維護(hù)內(nèi)存的方式品嚣。自己管理內(nèi)存極其自由炕倘,可以任意申請內(nèi)存,但如同一把雙刃劍翰撑,為大量內(nèi)存泄露罩旋,懸空指針等bug埋下隱患。 對于一個(gè)字符串眶诈、列表涨醋、類甚至數(shù)值都是對象,且定位簡單易用的語言逝撬,自然不會(huì)讓用戶去處理如何分配回收內(nèi)存的問題浴骂。 python里也同java一樣采用了垃圾收集機(jī)制,不過不一樣的是: python采用的是引用計(jì)數(shù)機(jī)制為主宪潮,標(biāo)記-清除和分代收集兩種機(jī)制為輔的策略

引用計(jì)數(shù)機(jī)制:

python里每一個(gè)東西都是對象溯警,它們的核心就是一個(gè)結(jié)構(gòu)體:PyObject

typedef struct_object {
    int ob_refcnt;
    struct_typeobject *ob_type;
} PyObject;

PyObject是每個(gè)對象必有的內(nèi)容,其中ob_refcnt就是做為引用計(jì)數(shù)狡相。當(dāng)一個(gè)對象有新的引用時(shí)梯轻,它的ob_refcnt就會(huì)增加,當(dāng)引用它的對象被刪除尽棕,它的ob_refcnt就會(huì)減少

#define Py_INCREF(op)   ((op)->ob_refcnt++) //增加計(jì)數(shù)
#define Py_DECREF(op) \ //減少計(jì)數(shù)
    if (--(op)->ob_refcnt != 0) \
        ; \
    else \
        __Py_Dealloc((PyObject *)(op))

當(dāng)引用計(jì)數(shù)為0時(shí)喳挑,該對象生命就結(jié)束了。

引用計(jì)數(shù)機(jī)制的優(yōu)點(diǎn):
  • 簡單
  • 實(shí)時(shí)性:一旦沒有引用滔悉,內(nèi)存就直接釋放了蟀悦。不用像其他機(jī)制等到特定時(shí)機(jī)。實(shí)時(shí)性還帶來一個(gè)好處:處理回收內(nèi)存的時(shí)間分?jǐn)偟搅似綍r(shí)氧敢。
引用計(jì)數(shù)機(jī)制的缺點(diǎn):
  • 維護(hù)引用計(jì)數(shù)消耗資源

  • 循環(huán)引用

    list1 = []
    list2 = []
    list1.append(list2)
    list2.append(list1)
    
    

list1與list2相互引用日戈,如果不存在其他對象對它們的引用,list1與list2的引用計(jì)數(shù)也仍然為1孙乖,所占用的內(nèi)存永遠(yuǎn)無法被回收浙炼,這將是致命的。 對于如今的強(qiáng)大硬件唯袄,缺點(diǎn)1尚可接受弯屈,但是循環(huán)引用導(dǎo)致內(nèi)存泄露,注定python還將引入新的回收機(jī)制恋拷。(標(biāo)記清除和分代收集)

2. 畫說 Ruby 與 Python 垃圾回收

英文原文: visualizing garbage collection in ruby and python

2.1 應(yīng)用程序那顆躍動(dòng)的心

GC系統(tǒng)所承擔(dān)的工作遠(yuǎn)比"垃圾回收"多得多资厉。實(shí)際上,它們負(fù)責(zé)三個(gè)重要任務(wù)蔬顾。它們

  • 為新生成的對象分配內(nèi)存
  • 識(shí)別那些垃圾對象宴偿,并且
  • 從垃圾對象那回收內(nèi)存湘捎。

如果將應(yīng)用程序比作人的身體:所有你所寫的那些優(yōu)雅的代碼,業(yè)務(wù)邏輯窄刘,算法窥妇,應(yīng)該就是大腦。以此類推娩践,垃圾回收機(jī)制應(yīng)該是那個(gè)身體器官呢活翩?(我從RuPy聽眾那聽到了不少有趣的答案:腰子、白血球 :) )

我認(rèn)為垃圾回收就是應(yīng)用程序那顆躍動(dòng)的心翻伺。像心臟為身體其他器官提供血液和營養(yǎng)物那樣材泄,垃圾回收器為你的應(yīng)該程序提供內(nèi)存和對象。如果心臟停跳吨岭,過不了幾秒鐘人就完了脸爱。如果垃圾回收器停止工作或運(yùn)行遲緩,像動(dòng)脈阻塞,你的應(yīng)用程序效率也會(huì)下降,直至最終死掉未妹。

2.2 一個(gè)簡單的例子

運(yùn)用實(shí)例一貫有助于理論的理解簿废。下面是一個(gè)簡單類,分別用Python和Ruby寫成络它,我們今天就以此為例:

day13_其他知識(shí)-01.png

順便提一句族檬,兩種語言的代碼竟能如此相像:Ruby 和 Python 在表達(dá)同一事物上真的只是略有不同。但是在這兩種語言的內(nèi)部實(shí)現(xiàn)上是否也如此相似呢化戳?

2.3 Ruby 的對象分配

當(dāng)我們執(zhí)行上面的Node.new(1)時(shí)单料,Ruby到底做了什么?Ruby是如何為我們創(chuàng)建新的對象的呢点楼? 出乎意料的是它做的非常少扫尖。實(shí)際上,早在代碼開始執(zhí)行前掠廓,Ruby就提前創(chuàng)建了成百上千個(gè)對象换怖,并把它們串在鏈表上,名曰:可用列表蟀瞧。下圖所示為可用列表的概念圖:

day13_其他知識(shí)-02.png

想象一下每個(gè)白色方格上都標(biāo)著一個(gè)"未使用預(yù)創(chuàng)建對象"沉颂。當(dāng)我們調(diào)用 Node.new ,Ruby只需取一個(gè)預(yù)創(chuàng)建對象給我們使用即可:

day13_其他知識(shí)-03.png

上圖中左側(cè)灰格表示我們代碼中使用的當(dāng)前對象,同時(shí)其他白格是未使用對象悦污。(請注意:無疑我的示意圖是對實(shí)際的簡化铸屉。實(shí)際上,Ruby會(huì)用另一個(gè)對象來裝載字符串"ABC",另一個(gè)對象裝載Node類定義切端,還有一個(gè)對象裝載了代碼中分析出的抽象語法樹彻坛,等等)

如果我們再次調(diào)用 Node.new,Ruby將遞給我們另一個(gè)對象:

day13_其他知識(shí)-04.png

這個(gè)簡單的用鏈表來預(yù)分配對象的算法已經(jīng)發(fā)明了超過50年,而發(fā)明人這是赫赫有名的計(jì)算機(jī)科學(xué)家John McCarthy昌屉,一開始是用Lisp實(shí)現(xiàn)的钙蒙。Lisp不僅是最早的函數(shù)式編程語言,在計(jì)算機(jī)科學(xué)領(lǐng)域也有許多創(chuàng)舉怠益。其一就是利用垃圾回收機(jī)制自動(dòng)化進(jìn)行程序內(nèi)存管理的概念。

標(biāo)準(zhǔn)版的Ruby瘾婿,也就是眾所周知的"Matz's Ruby Interpreter"(MRI),所使用的GC算法與McCarthy在1960年的實(shí)現(xiàn)方式很類似蜻牢。無論好壞,Ruby的垃圾回收機(jī)制已經(jīng)53歲高齡了偏陪。像Lisp一樣抢呆,Ruby預(yù)先創(chuàng)建一些對象,然后在你分配新對象或者變量的時(shí)候供你使用笛谦。

2.4 Python 的對象分配

我們已經(jīng)了解了Ruby預(yù)先創(chuàng)建對象并將它們存放在可用列表中抱虐。那Python又怎么樣呢?

盡管由于許多原因Python也使用可用列表(用來回收一些特定對象比如 list)饥脑,但在為新對象和變量分配內(nèi)存的方面Python和Ruby是不同的恳邀。

例如我們用Pyhon來創(chuàng)建一個(gè)Node對象:

day13_其他知識(shí)-05.png

與Ruby不同,當(dāng)創(chuàng)建對象時(shí)Python立即向操作系統(tǒng)請求內(nèi)存灶轰。(Python實(shí)際上實(shí)現(xiàn)了一套自己的內(nèi)存分配系統(tǒng)谣沸,在操作系統(tǒng)堆之上提供了一個(gè)抽象層。但是我今天不展開說了笋颤。)

當(dāng)我們創(chuàng)建第二個(gè)對象的時(shí)候乳附,再次像OS請求內(nèi)存:

day13_其他知識(shí)-06.png

看起來夠簡單吧,在我們創(chuàng)建對象的時(shí)候伴澄,Python會(huì)花些時(shí)間為我們找到并分配內(nèi)存赋除。

2.5 Ruby 開發(fā)者住在凌亂的房間里

day13_其他知識(shí)-07.jpg

Ruby把無用的對象留在內(nèi)存里,直到下一次GC執(zhí)行

回過來看Ruby非凌。隨著我們創(chuàng)建越來越多的對象举农,Ruby會(huì)持續(xù)尋可用列表里取預(yù)創(chuàng)建對象給我們。因此敞嗡,可用列表會(huì)逐漸變短:

day13_其他知識(shí)-08.png

...然后更短:

day13_其他知識(shí)-09.png

請注意我一直在為變量n1賦新值并蝗,Ruby把舊值留在原處。"ABC","JKL"和"MNO"三個(gè)Node實(shí)例還滯留在內(nèi)存中秸妥。Ruby不會(huì)立即清除代碼中不再使用的舊對象滚停!Ruby開發(fā)者們就像是住在一間凌亂的房間,地板上摞著衣服粥惧,要么洗碗池里都是臟盤子键畴。作為一個(gè)Ruby程序員,無用的垃圾對象會(huì)一直環(huán)繞著你。

2.6 Python 開發(fā)者住在衛(wèi)生之家庭

day13_其他知識(shí)-10.jpg

用完的垃圾對象會(huì)立即被Python打掃干凈

Python與Ruby的垃圾回收機(jī)制頗為不同起惕。讓我們回到前面提到的三個(gè)Python Node對象:

day13_其他知識(shí)-11.png

在內(nèi)部涡贱,創(chuàng)建一個(gè)對象時(shí),Python總是在對象的C結(jié)構(gòu)體里保存一個(gè)整數(shù)惹想,稱為 引用數(shù)问词。期初,Python將這個(gè)值設(shè)置為1:

day13_其他知識(shí)-12.png

值為1說明分別有個(gè)一個(gè)指針指向或是引用這三個(gè)對象嘀粱。假如我們現(xiàn)在創(chuàng)建一個(gè)新的Node實(shí)例激挪,JKL:

day13_其他知識(shí)-13.png

與之前一樣,Python設(shè)置JKL的引用數(shù)為1锋叨。然而垄分,請注意由于我們改變了n1指向了JKL,不再指向ABC娃磺,Python就把ABC的引用數(shù)置為0了薄湿。 此刻,Python垃圾回收器立刻挺身而出偷卧!每當(dāng)對象的引用數(shù)減為0豺瘤,Python立即將其釋放,把內(nèi)存還給操作系統(tǒng):

day13_其他知識(shí)-14.png

上面Python回收了ABC Node實(shí)例使用的內(nèi)存听诸。記住炉奴,Ruby棄舊對象原地于不顧,也不釋放它們的內(nèi)存蛇更。

Python的這種垃圾回收算法被稱為引用計(jì)數(shù)瞻赶。是George-Collins在1960年發(fā)明的,恰巧與John McCarthy發(fā)明的可用列表算法在同一年出現(xiàn)派任。就像Mike-Bernstein在6月份哥譚市Ruby大會(huì)杰出的垃圾回收機(jī)制演講中說的: "1960年是垃圾收集器的黃金年代..."

Python開發(fā)者工作在衛(wèi)生之家,你可以想象砸逊,有個(gè)患有輕度OCD(一種強(qiáng)迫癥)的室友一刻不停地跟在你身后打掃,你一放下臟碟子或杯子掌逛,有個(gè)家伙已經(jīng)準(zhǔn)備好把它放進(jìn)洗碗機(jī)了师逸!

現(xiàn)在來看第二例子。加入我們讓n2引用n1:

day13_其他知識(shí)-15.png

上圖中左邊的DEF的引用數(shù)已經(jīng)被Python減少了豆混,垃圾回收器會(huì)立即回收DEF實(shí)例篓像。同時(shí)JKL的引用數(shù)已經(jīng)變?yōu)榱? ,因?yàn)閚1和n2都指向它皿伺。

2.7 標(biāo)記-清除

最終那間凌亂的房間充斥著垃圾员辩,再不能歲月靜好了。在Ruby程序運(yùn)行了一陣子以后鸵鸥,可用列表最終被用光光了:

day13_其他知識(shí)-16.png

此刻所有Ruby預(yù)創(chuàng)建對象都被程序用過了(它們都變灰了)奠滑,可用列表里空空如也(沒有白格子了)丹皱。

此刻Ruby祭出另一McCarthy發(fā)明的算法,名曰:標(biāo)記-清除宋税。首先Ruby把程序停下來摊崭,Ruby用"地球停轉(zhuǎn)垃圾回收大法"。之后Ruby輪詢所有指針杰赛,變量和代碼產(chǎn)生別的引用對象和其他值呢簸。同時(shí)Ruby通過自身的虛擬機(jī)便利內(nèi)部指針。標(biāo)記出這些指針引用的每個(gè)對象乏屯。我在圖中使用M表示根时。

day13_其他知識(shí)-17.png

上圖中那三個(gè)被標(biāo)M的對象是程序還在使用的。在內(nèi)部瓶珊,Ruby實(shí)際上使用一串位值啸箫,被稱為:可用位圖(譯注:還記得《編程珠璣》里的為突發(fā)排序嗎耸彪,這對離散度不高的有限整數(shù)集合具有很強(qiáng)的壓縮效果伞芹,用以節(jié)約機(jī)器的資源。)蝉娜,來跟蹤對象是否被標(biāo)記了唱较。

day13_其他知識(shí)-18.png

如果說被標(biāo)記的對象是存活的,剩下的未被標(biāo)記的對象只能是垃圾召川,這意味著我們的代碼不再會(huì)使用它了南缓。我會(huì)在下圖中用白格子表示垃圾對象:

day13_其他知識(shí)-19.png

接下來Ruby清除這些無用的垃圾對象,把它們送回到可用列表中:

day13_其他知識(shí)-20.png

在內(nèi)部這一切發(fā)生得迅雷不及掩耳荧呐,因?yàn)镽uby實(shí)際上不會(huì)吧對象從這拷貝到那汉形。而是通過調(diào)整內(nèi)部指針,將其指向一個(gè)新鏈表的方式倍阐,來將垃圾對象歸位到可用列表中的概疆。

現(xiàn)在等到下回再創(chuàng)建對象的時(shí)候Ruby又可以把這些垃圾對象分給我們使用了妆绞。在Ruby里宿稀,對象們六道輪回,轉(zhuǎn)世投胎篡石,享受多次人生概耻。

2.8 標(biāo)記-刪除 vs. 引用計(jì)數(shù)

乍一看使套,Python的GC算法貌似遠(yuǎn)勝于Ruby的:寧舍潔宇而居穢室乎?為什么Ruby寧愿定期強(qiáng)制程序停止運(yùn)行鞠柄,也不使用Python的算法呢侦高?

然而,引用計(jì)數(shù)并不像第一眼看上去那樣簡單厌杜。有許多原因使得不許多語言不像Python這樣使用引用計(jì)數(shù)GC算法:

首先矫膨,它不好實(shí)現(xiàn)。Python不得不在每個(gè)對象內(nèi)部留一些空間來處理引用數(shù)。這樣付出了一小點(diǎn)兒空間上的代價(jià)侧馅。但更糟糕的是危尿,每個(gè)簡單的操作(像修改變量或引用)都會(huì)變成一個(gè)更復(fù)雜的操作,因?yàn)镻ython需要增加一個(gè)計(jì)數(shù)馁痴,減少另一個(gè)谊娇,還可能釋放對象。

第二點(diǎn)罗晕,它相對較慢济欢。雖然Python隨著程序執(zhí)行GC很穩(wěn)健(一把臟碟子放在洗碗盆里就開始洗啦)小渊,但這并不一定更快法褥。Python不停地更新著眾多引用數(shù)值。特別是當(dāng)你不再使用一個(gè)大數(shù)據(jù)結(jié)構(gòu)的時(shí)候酬屉,比如一個(gè)包含很多元素的列表半等,Python可能必須一次性釋放大量對象。減少引用數(shù)就成了一項(xiàng)復(fù)雜的遞歸過程了呐萨。

最后杀饵,它不是總奏效的。引用計(jì)數(shù)不能處理環(huán)形數(shù)據(jù)結(jié)構(gòu)--也就是含有循環(huán)引用的數(shù)據(jù)結(jié)構(gòu)谬擦。

3. Python中的循環(huán)數(shù)據(jù)結(jié)構(gòu)以及引用計(jì)數(shù)

3.1 循環(huán)引用

通過上篇切距,我們知道在Python中,每個(gè)對象都保存了一個(gè)稱為引用計(jì)數(shù)的整數(shù)值惨远,來追蹤到底有多少引用指向了這個(gè)對象谜悟。無論何時(shí),如果我們程序中的一個(gè)變量或其他對象引用了目標(biāo)對象北秽,Python將會(huì)增加這個(gè)計(jì)數(shù)值葡幸,而當(dāng)程序停止使用這個(gè)對象,則Python會(huì)減少這個(gè)計(jì)數(shù)值羡儿。一旦計(jì)數(shù)值被減到零礼患,Python將會(huì)釋放這個(gè)對象以及回收相關(guān)內(nèi)存空間。

從六十年代開始掠归,計(jì)算機(jī)科學(xué)界就面臨了一個(gè)嚴(yán)重的理論問題缅叠,那就是針對引用計(jì)數(shù)這種算法來說,如果一個(gè)數(shù)據(jù)結(jié)構(gòu)引用了它自身虏冻,即如果這個(gè)數(shù)據(jù)結(jié)構(gòu)是一個(gè)循環(huán)數(shù)據(jù)結(jié)構(gòu)肤粱,那么某些引用計(jì)數(shù)值是肯定無法變成零的。為了更好地理解這個(gè)問題厨相,讓我們舉個(gè)例子领曼。下面的代碼展示了一些上周我們所用到的節(jié)點(diǎn)類:

day13_其他知識(shí)-21.jpg

我們有一個(gè)"構(gòu)造器"(在Python中叫做 init )鸥鹉,在一個(gè)實(shí)例變量中存儲(chǔ)一個(gè)單獨(dú)的屬性。在類定義之后我們創(chuàng)建兩個(gè)節(jié)點(diǎn)庶骄,ABC以及DEF毁渗,在圖中為左邊的矩形框。兩個(gè)節(jié)點(diǎn)的引用計(jì)數(shù)都被初始化為1单刁,因?yàn)楦饔袃蓚€(gè)引用指向各個(gè)節(jié)點(diǎn)(n1和n2)灸异。

現(xiàn)在,讓我們在節(jié)點(diǎn)中定義兩個(gè)附加的屬性羔飞,next以及prev:

day13_其他知識(shí)-22.jpg

跟Ruby不同的是肺樟,Python中你可以在代碼運(yùn)行的時(shí)候動(dòng)態(tài)定義實(shí)例變量或?qū)ο髮傩浴_@看起來似乎有點(diǎn)像Ruby缺失了某些有趣的魔法逻淌。(聲明下我不是一個(gè)Python程序員么伯,所以可能會(huì)存在一些命名方面的錯(cuò)誤)。我們設(shè)置 n1.next 指向 n2卡儒,同時(shí)設(shè)置 n2.prev 指回 n1√锶幔現(xiàn)在,我們的兩個(gè)節(jié)點(diǎn)使用循環(huán)引用的方式構(gòu)成了一個(gè)雙向鏈表朋贬。同時(shí)請注意到 ABC 以及 DEF 的引用計(jì)數(shù)值已經(jīng)增加到了2凯楔。這里有兩個(gè)指針指向了每個(gè)節(jié)點(diǎn):首先是 n1 以及 n2窜骄,其次就是 next 以及 prev锦募。

現(xiàn)在,假定我們的程序不再使用這兩個(gè)節(jié)點(diǎn)了邻遏,我們將 n1 和 n2 都設(shè)置為null(Python中是None)糠亩。

day13_其他知識(shí)-23.jpg

好了,Python會(huì)像往常一樣將每個(gè)節(jié)點(diǎn)的引用計(jì)數(shù)減少到1准验。

3.2 在Python中的零代(Generation Zero)

請注意在以上剛剛說到的例子中赎线,我們以一個(gè)不是很常見的情況結(jié)尾:我們有一個(gè)“孤島”或是一組未使用的、互相指向的對象糊饱,但是誰都沒有外部引用垂寥。換句話說,我們的程序不再使用這些節(jié)點(diǎn)對象了另锋,所以我們希望Python的垃圾回收機(jī)制能夠足夠智能去釋放這些對象并回收它們占用的內(nèi)存空間滞项。但是這不可能,因?yàn)樗械囊糜?jì)數(shù)都是1而不是0夭坪。Python的引用計(jì)數(shù)算法不能夠處理互相指向自己的對象文判。

這就是為什么Python要引入Generational GC算法的原因!正如Ruby使用一個(gè)鏈表(free list)來持續(xù)追蹤未使用的室梅、自由的對象一樣戏仓,Python使用一種不同的鏈表來持續(xù)追蹤活躍的對象疚宇。而不將其稱之為“活躍列表”,Python的內(nèi)部C代碼將其稱為零代(Generation Zero)赏殃。每次當(dāng)你創(chuàng)建一個(gè)對象或其他什么值的時(shí)候敷待,Python會(huì)將其加入零代鏈表:

day13_其他知識(shí)-24.jpg

從上邊可以看到當(dāng)我們創(chuàng)建ABC節(jié)點(diǎn)的時(shí)候,Python將其加入零代鏈表仁热。請注意到這并不是一個(gè)真正的列表讼撒,并不能直接在你的代碼中訪問,事實(shí)上這個(gè)鏈表是一個(gè)完全內(nèi)部的Python運(yùn)行時(shí)股耽。 相似的根盒,當(dāng)我們創(chuàng)建DEF節(jié)點(diǎn)的時(shí)候,Python將其加入同樣的鏈表:

day13_其他知識(shí)-25.jpg

現(xiàn)在零代包含了兩個(gè)節(jié)點(diǎn)對象物蝙。(他還將包含Python創(chuàng)建的每個(gè)其他值炎滞,與一些Python自己使用的內(nèi)部值。)

3.3 檢測循環(huán)引用

隨后诬乞,Python會(huì)循環(huán)遍歷零代列表上的每個(gè)對象册赛,檢查列表中每個(gè)互相引用的對象,根據(jù)規(guī)則減掉其引用計(jì)數(shù)震嫉。在這個(gè)過程中森瘪,Python會(huì)一個(gè)接一個(gè)的統(tǒng)計(jì)內(nèi)部引用的數(shù)量以防過早地釋放對象。

為了便于理解票堵,來看一個(gè)例子:

day13_其他知識(shí)-27.jpg

從上面可以看到 ABC 和 DEF 節(jié)點(diǎn)包含的引用數(shù)為1.有三個(gè)其他的對象同時(shí)存在于零代鏈表中扼睬,藍(lán)色的箭頭指示了有一些對象正在被零代鏈表之外的其他對象所引用。(接下來我們會(huì)看到悴势,Python中同時(shí)存在另外兩個(gè)分別被稱為一代和二代的鏈表)窗宇。這些對象有著更高的引用計(jì)數(shù)因?yàn)樗鼈冋诒黄渌羔標(biāo)赶蛑?/p>

接下來你會(huì)看到Python的GC是如何處理零代鏈表的。

day13_其他知識(shí)-28.jpg

通過識(shí)別內(nèi)部引用特纤,Python能夠減少許多零代鏈表對象的引用計(jì)數(shù)军俊。在上圖的第一行中你能夠看見ABC和DEF的引用計(jì)數(shù)已經(jīng)變?yōu)榱懔耍@意味著收集器可以釋放它們并回收內(nèi)存空間了捧存。剩下的活躍的對象則被移動(dòng)到一個(gè)新的鏈表:一代鏈表粪躬。

從某種意義上說,Python的GC算法類似于Ruby所用的標(biāo)記回收算法昔穴。周期性地從一個(gè)對象到另一個(gè)對象追蹤引用以確定對象是否還是活躍的镰官,正在被程序所使用的,這正類似于Ruby的標(biāo)記過程傻咖。

Python中的GC閾值

Python什么時(shí)候會(huì)進(jìn)行這個(gè)標(biāo)記過程朋魔?隨著你的程序運(yùn)行,Python解釋器保持對新創(chuàng)建的對象卿操,以及因?yàn)橐糜?jì)數(shù)為零而被釋放掉的對象的追蹤警检。從理論上說孙援,這兩個(gè)值應(yīng)該保持一致,因?yàn)槌绦蛐陆ǖ拿總€(gè)對象都應(yīng)該最終被釋放掉扇雕。

當(dāng)然拓售,事實(shí)并非如此。因?yàn)檠h(huán)引用的原因镶奉,并且因?yàn)槟愕某绦蚴褂昧艘恍┍绕渌麑ο蟠嬖跁r(shí)間更長的對象础淤,從而被分配對象的計(jì)數(shù)值與被釋放對象的計(jì)數(shù)值之間的差異在逐漸增長。一旦這個(gè)差異累計(jì)超過某個(gè)閾值哨苛,則Python的收集機(jī)制就啟動(dòng)了鸽凶,并且觸發(fā)上邊所說到的零代算法,釋放“浮動(dòng)的垃圾”建峭,并且將剩下的對象移動(dòng)到一代列表玻侥。

隨著時(shí)間的推移,程序所使用的對象逐漸從零代列表移動(dòng)到一代列表亿蒸。而Python對于一代列表中對象的處理遵循同樣的方法凑兰,一旦被分配計(jì)數(shù)值與被釋放計(jì)數(shù)值累計(jì)到達(dá)一定閾值,Python會(huì)將剩下的活躍對象移動(dòng)到二代列表边锁。

通過這種方法姑食,你的代碼所長期使用的對象,那些你的代碼持續(xù)訪問的活躍對象茅坛,會(huì)從零代鏈表轉(zhuǎn)移到一代再轉(zhuǎn)移到二代音半。通過不同的閾值設(shè)置,Python可以在不同的時(shí)間間隔處理這些對象灰蛙。Python處理零代最為頻繁祟剔,其次是一代然后才是二代隔躲。

弱代假說

來看看代垃圾回收算法的核心行為:垃圾回收器會(huì)更頻繁的處理新對象摩梧。一個(gè)新的對象即是你的程序剛剛創(chuàng)建的,而一個(gè)來的對象則是經(jīng)過了幾個(gè)時(shí)間周期之后仍然存在的對象宣旱。Python會(huì)在當(dāng)一個(gè)對象從零代移動(dòng)到一代仅父,或是從一代移動(dòng)到二代的過程中提升(promote)這個(gè)對象。

為什么要這么做浑吟?這種算法的根源來自于弱代假說(weak generational hypothesis)笙纤。這個(gè)假說由兩個(gè)觀點(diǎn)構(gòu)成:首先是年親的對象通常死得也快,而老對象則很有可能存活更長的時(shí)間组力。

假定現(xiàn)在我用Python或是Ruby創(chuàng)建一個(gè)新對象:

day13_其他知識(shí)-29.jpg

根據(jù)假說省容,我的代碼很可能僅僅會(huì)使用ABC很短的時(shí)間。這個(gè)對象也許僅僅只是一個(gè)方法中的中間結(jié)果燎字,并且隨著方法的返回這個(gè)對象就將變成垃圾了腥椒。大部分的新對象都是如此般地很快變成垃圾阿宅。然而,偶爾程序會(huì)創(chuàng)建一些很重要的笼蛛,存活時(shí)間比較長的對象-例如web應(yīng)用中的session變量或是配置項(xiàng)洒放。

通過頻繁的處理零代鏈表中的新對象,Python的垃圾收集器將把時(shí)間花在更有意義的地方:它處理那些很快就可能變成垃圾的新對象滨砍。同時(shí)只在很少的時(shí)候往湿,當(dāng)滿足閾值的條件,收集器才回去處理那些老變量惋戏。


下一篇文章為:→1.3.3垃圾回收(三)-gc模塊
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末领追,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子响逢,更是在濱河造成了極大的恐慌蔓腐,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件龄句,死亡現(xiàn)場離奇詭異回论,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)分歇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門傀蓉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人职抡,你說我怎么就攤上這事葬燎。” “怎么了缚甩?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵谱净,是天一觀的道長。 經(jīng)常有香客問我擅威,道長壕探,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任郊丛,我火速辦了婚禮李请,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘厉熟。我一直安慰自己导盅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布揍瑟。 她就那樣靜靜地躺著白翻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绢片。 梳的紋絲不亂的頭發(fā)上滤馍,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天恩急,我揣著相機(jī)與錄音,去河邊找鬼纪蜒。 笑死衷恭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的纯续。 我是一名探鬼主播随珠,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼猬错!你這毒婦竟也來了窗看?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤倦炒,失蹤者是張志新(化名)和其女友劉穎显沈,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逢唤,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拉讯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鳖藕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魔慷。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖著恩,靈堂內(nèi)的尸體忽然破棺而出院尔,到底是詐尸還是另有隱情,我是刑警寧澤喉誊,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布邀摆,位于F島的核電站,受9級(jí)特大地震影響伍茄,放射性物質(zhì)發(fā)生泄漏栋盹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一幻林、第九天 我趴在偏房一處隱蔽的房頂上張望贞盯。 院中可真熱鬧,春花似錦沪饺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至讥脐,卻和暖如春遭居,著一層夾襖步出監(jiān)牢的瞬間啼器,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工俱萍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留端壳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓枪蘑,卻偏偏與公主長得像损谦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子岳颇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 雖然是自己轉(zhuǎn)載的但是是真的好的一篇圖文并茂的對垃圾回收機(jī)制的講解!!! 先來個(gè)概述照捡,第二部分的畫述才是厲害的。 G...
    東皇Amrzs閱讀 118,644評論 13 176
  • 一元類 1類也是對象 在大多數(shù)編程語言中,類就是一組用來描述如何生成一個(gè)對象的代碼段瞻鹏。在Python中這一點(diǎn)仍然成...
    五行缺覺閱讀 1,052評論 0 1
  • 1.元類 1.1.1類也是對象 在大多數(shù)編程語言中悲立,類就是一組用來描述如何生成一個(gè)對象的代碼段。在Python中這...
    TENG書閱讀 1,264評論 0 3
  • 軟件項(xiàng)目管理中10大知識(shí)領(lǐng)域中(整合、范圍叭披、時(shí)間寥殖、成本、質(zhì)量涩蜘、人力資源嚼贡、溝通、風(fēng)險(xiǎn)同诫、采購粤策、干系人)項(xiàng)目資源...
    Smart熊大閱讀 4,881評論 1 8
  • 9月5號(hào)好種子的發(fā)芽 昨天和我朋友吃飯,之后幫朋友的朋友搬家误窖,從來沒見過那位需要搬家的主人叮盘,主人回國了不回加拿大了...
    廢迪迪閱讀 141評論 0 1