python 垃圾回收

Python的GC模塊主要運(yùn)用了“引用計數(shù)”(reference counting)來跟蹤和回收垃圾。在引用計數(shù)的基礎(chǔ)上,還可以通過“標(biāo)記-清除”(mark and sweep)解決容器對象可能產(chǎn)生的循環(huán)引用的問題。通過“分代回收”(generation collection)以空間換取時間來進(jìn)一步提高垃圾回收的效率。

一、引用計數(shù)

原理:當(dāng)一個對象的引用被創(chuàng)建或者復(fù)制時,對象的引用計數(shù)加1;當(dāng)一個對象的引用被銷毀時,對象的引用計數(shù)減1;當(dāng)對象的引用計數(shù)減少為0時,就意味著對象已經(jīng)沒有被任何人使用了,可以將其所占用的內(nèi)存釋放了。

優(yōu)點(diǎn):實(shí)時性,任何內(nèi)存,一旦沒有指向它的引用,就會立即被回收。而其他的垃圾收集計數(shù)必須在某種特殊條件下(比如內(nèi)存分配失?。┎拍苓M(jìn)行無效內(nèi)存的回收。

缺點(diǎn):執(zhí)行效率不高,引用計數(shù)機(jī)制所帶來的維護(hù)引用計數(shù)的額外操作與Python運(yùn)行中所進(jìn)行的內(nèi)存分配和釋放,引用賦值的次數(shù)是成正比的。而這點(diǎn)相比其他主流的垃圾回收機(jī)制,比如“標(biāo)記-清除”,“停止-復(fù)制”,是一個弱點(diǎn),因?yàn)檫@些技術(shù)所帶來的額外操作基本上只是與待回收的內(nèi)存數(shù)量有關(guān)。

致命問題:循環(huán)引用(也稱交叉引用),循環(huán)引用可以使一組對象的引用計數(shù)不為0,然而這些對象實(shí)際上并沒有被任何外部對象所引用,它們之間只是相互引用。這意味著不會再有人使用這組對象,應(yīng)該回收這組對象所占用的內(nèi)存空間,然后由于相互引用的存在,每一個對象的引用計數(shù)都不為0,因此這些對象所占用的內(nèi)存永遠(yuǎn)不會被釋放。

eg:

a = []
b = []
a.append(b)
b.append(b)
print a
[[[…]]]
print b
[[[…]]]

二、標(biāo)記清除機(jī)制

標(biāo)記-清除”是為了解決循環(huán)引用的問題??梢园渌麑ο笠玫娜萜鲗ο螅ū热纾簂ist,set,dict,class,instance)都可能產(chǎn)生循環(huán)引用。

如果兩個對象的引用計數(shù)都為1,但是僅僅存在他們之間的循環(huán)引用,那么這兩個對象都是需要被回收的,也就是說,它們的引用計數(shù)雖然表現(xiàn)為非0,但實(shí)際上有效的引用計數(shù)為0。我們必須先將循環(huán)引用摘掉,那么這兩個對象的有效計數(shù)就現(xiàn)身了。假設(shè)兩個對象為A、B,我們從A出發(fā),因?yàn)樗幸粋€對B的引用,則將B的引用計數(shù)減1;然后順著引用達(dá)到B,因?yàn)锽有一個對A的引用,同樣將A的引用減1,這樣,就完成了循環(huán)引用對象間環(huán)摘除。

這里就會出現(xiàn)一個問題,如果A引用了B,B沒引用A,從A出發(fā)刪除B的引用使得B的引用值為0,導(dǎo)致最后B被回收,如果A還存在,那么就會導(dǎo)致A指向Bde指針懸空引用。為了防止這種情況情況的發(fā)生,python在標(biāo)記清楚機(jī)制中采取了不改動真實(shí)引用計數(shù)而只改動引用計數(shù)副本的方法。

eg:

#第一組循環(huán)引用#
a = [1,2]
b = [3,4]
a.append(b)
b.append(a)
#刪除a不刪除b,b引用a,此時a和b都不能被回收
del a

#第二組循環(huán)引用#
c = [4,5]
d = [5,6]
c.append(d)
d.append(c)
#刪除c,d cd互相引用,應(yīng)該都被回收
del c
del d
#至此,原a和原c和原d所引用的對象的引用計數(shù)都為1,b所引用的對象的引用計數(shù)為2,

首先,將對象分為兩撥,一撥叫root object(存活組),一撥叫unreachable(死亡組)。然后,他把各個對象的引用計數(shù)復(fù)制出來,對這個副本進(jìn)行引用環(huán)的摘除。摘除完畢,此時a的引用計數(shù)的副本是0,b的引用計數(shù)的副本是1,c和d的引用計數(shù)的副本都是0。那么先把副本為非0的放到存活組,副本為0的打入死亡組。如果就這樣結(jié)束的話,就錯殺了a了,因?yàn)閎還要用,我們把a(bǔ)所引用的對象在內(nèi)存中清除了b還能用嗎?顯然還得在審一遍,別把無辜的人也給殺了,于是他就在存活組里,對每個對象都分析一遍,由于目前存活組只有b,那么他只對b分析,因?yàn)閎要存活,所以b里的元素也要存活,于是在b中就發(fā)現(xiàn)了原a所指向的對象,于是就把他從死亡組中解救出來。至此,進(jìn)過了一審和二審,最終把所有的任然在死亡組中的對象通通殺掉,而root object繼續(xù)存活。b所指向的對象引用計數(shù)任然是2,原a所指向的對象的引用計數(shù)仍然是1。

三、分代回收

分代的垃圾收集技術(shù)是在上個世紀(jì)80年代初發(fā)展起來的一種垃圾收集機(jī)制,一系列的研究表明:無論使用何種語言開發(fā),無論開發(fā)的是何種類型,何種規(guī)模的程序,都存在這樣一點(diǎn)相同之處。即:一定比例的內(nèi)存塊的生存周期都比較短,通常是幾百萬條機(jī)器指令的時間,而剩下的內(nèi)存塊,起生存周期比較長,甚至?xí)某绦蜷_始一直持續(xù)到程序結(jié)束。

從前面“標(biāo)記-清除”這樣的垃圾收集機(jī)制來看,這種垃圾收集機(jī)制所帶來的額外操作實(shí)際上與系統(tǒng)中總的內(nèi)存塊的數(shù)量是相關(guān)的,當(dāng)需要回收的內(nèi)存塊越多時,垃圾檢測帶來的額外操作就越多,而垃圾回收帶來的額外操作就越少;反之,當(dāng)需回收的內(nèi)存塊越少時,垃圾檢測就將比垃圾回收帶來更少的額外操作。為了提高垃圾收集的效率,采用“空間換時間的策略”。

原理:將系統(tǒng)中的所有內(nèi)存塊根據(jù)其存活時間劃分為不同的集合,每一個集合就成為一個“代”,垃圾收集的頻率隨著“代”的存活時間的增大而減小。也就是說,活得越長的對象,就越不可能是垃圾,就應(yīng)該減少對它的垃圾收集頻率。那么如何來衡量這個存活時間:通常是利用幾次垃圾收集動作來衡量,如果一個對象經(jīng)過的垃圾收集次數(shù)越多,可以得出:該對象存活時間就越長。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 雖然是自己轉(zhuǎn)載的但是是真的好的一篇圖文并茂的對垃圾回收機(jī)制的講解!!! 先來個概述,第二部分的畫述才是厲害的。 G...
    東皇Amrzs閱讀 119,292評論 13 175
  • 現(xiàn)在的高級語言如java,c#等,都采用了垃圾收集機(jī)制,而不再是c,c++里用戶自己管理維護(hù)內(nèi)存的方式。但是這種方...
    LittlePy閱讀 807評論 0 1
  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虛擬機(jī)(JVM)垃圾回收器提供...
    簡欲明心閱讀 90,359評論 17 311
  • 你曾經(jīng)在我不乖的時候,說夫?yàn)槠蘧V,我應(yīng)該要聽你的話,不要鬧。
    南瓜不說話zws閱讀 176評論 0 0
  • 情態(tài)動詞術(shù)語解釋 MUST必須、一定要;MUST NOT禁止;REQUIRED需要;SHALL、SHOULD應(yīng)該;...
    霜之幽語閱讀 1,964評論 0 0

友情鏈接更多精彩內(nèi)容