- Python的GC模塊主要運(yùn)用了引用計(jì)數(shù)來(lái)跟蹤和回收垃圾。在引用計(jì)數(shù)的基礎(chǔ)上,還可以通過(guò)“標(biāo)記-清除”解決容器對(duì)象可能產(chǎn)生的循環(huán)引用的問(wèn)題。通過(guò)分代回收以空間換取時(shí)間進(jìn)一步提高垃圾回收的效率。
引用計(jì)數(shù)
- 原理:當(dāng)一個(gè)對(duì)象的引用被創(chuàng)建或者復(fù)制時(shí),對(duì)象的引用計(jì)數(shù)加1;當(dāng)一個(gè)對(duì)象的引用被銷(xiāo)毀時(shí),對(duì)象的引用計(jì)數(shù)減1。當(dāng)對(duì)象的引用計(jì)數(shù)減少為0時(shí),就意味著對(duì)象已經(jīng)再?zèng)]有被使用了,可以將其內(nèi)存釋放掉。
- 優(yōu)點(diǎn):引用計(jì)數(shù)有一個(gè)很大的優(yōu)點(diǎn),即實(shí)時(shí)性,任何內(nèi)存,一旦沒(méi)有指向它的引用,就會(huì)被立即回收,而其他的垃圾收集技術(shù)必須在某種特殊條件下才能進(jìn)行無(wú)效內(nèi)存的回收。
- 缺點(diǎn):但是它也有弱點(diǎn),引用計(jì)數(shù)機(jī)制所帶來(lái)的維護(hù)引用計(jì)數(shù)的額外操作與Python運(yùn)行中所進(jìn)行的內(nèi)存分配和釋放、引用賦值的次數(shù)是成正比的,這顯然比其它那些垃圾收集技術(shù)所帶來(lái)的額外操作只是與待回收的內(nèi)存數(shù)量有關(guān)的效率要低。同時(shí),引用技術(shù)還存在另外一個(gè)很大的問(wèn)題-循環(huán)引用,因?yàn)閷?duì)象之間相互引用,每個(gè)對(duì)象的引用都不會(huì)為0,所以這些對(duì)象所占用的內(nèi)存始終都不會(huì)被釋放掉。
何為循環(huán)引用

循環(huán)引用.png
- 不可變對(duì)象是不可能產(chǎn)生循環(huán)引用的,因?yàn)樗鼈儍?nèi)部不可能持有其它對(duì)象的引用。Python中的循環(huán)引用總是發(fā)生在container對(duì)象之間,也就是能夠在內(nèi)部持有其它對(duì)象的對(duì)象,比如list、dict、class等等。
- a與b相互引用,如果不存在其他對(duì)象對(duì)它們的引用,a與b的引用計(jì)數(shù)也仍然為1,所占用的內(nèi)存永遠(yuǎn)無(wú)法被回收,還會(huì)導(dǎo)致內(nèi)存泄露
標(biāo)記-清除
- 原理:
- 尋找根對(duì)象(root object)的集合作為垃圾檢測(cè)動(dòng)作的起點(diǎn),根對(duì)象也就是一些全局引用和函數(shù)棧中的引用,這些引用所指向的對(duì)象是不可被刪除的;
- 從root object集合出發(fā),沿著root object集合中的每一個(gè)引用,如果能夠到達(dá)某個(gè)對(duì)象,則說(shuō)明這個(gè)對(duì)象是可達(dá)的,那么就不會(huì)被刪除,這個(gè)過(guò)程就是垃圾檢測(cè)階段;
- 當(dāng)檢測(cè)階段結(jié)束以后,所有的對(duì)象就分成可達(dá)和不可達(dá)兩部分,所有的可達(dá)對(duì)象都進(jìn)行保留,其它的不可達(dá)對(duì)象所占用的內(nèi)存將會(huì)被回收,這就是垃圾回收階段。(底層采用的是鏈表將這些集合的對(duì)象連接在一起)
- 缺點(diǎn):標(biāo)記和清除的過(guò)程效率不高。
分代回收
- 原理:將系統(tǒng)中的所有內(nèi)存塊根據(jù)其存活時(shí)間劃分為不同的集合,每一個(gè)集合就成為一個(gè)“代”,Python默認(rèn)定義了三代對(duì)象集合,垃圾收集的頻率隨著“代”的存活時(shí)間的增大而減小。也就是說(shuō),活得越長(zhǎng)的對(duì)象,就越不可能是垃圾,就應(yīng)該減少對(duì)它的垃圾收集頻率。那么如何來(lái)衡量這個(gè)存活時(shí)間:通常是利用幾次垃圾收集動(dòng)作來(lái)衡量,如果一個(gè)對(duì)象經(jīng)過(guò)的垃圾收集次數(shù)越多,可以得出:該對(duì)象存活時(shí)間就越長(zhǎng)。