引用計(jì)數(shù)
在Python源碼中,每一個(gè)對(duì)象都是一個(gè)結(jié)構(gòu)體表示,都有一個(gè)計(jì)數(shù)字段。
typedef struct_object { int ob_refcnt; struct_typeobject *ob_type; } PyObject;
PyObject是每個(gè)對(duì)象必有的內(nèi)容,其中ob_refcnt就是作為引用計(jì)數(shù)。當(dāng)一個(gè)對(duì)象有了新的引用時(shí),它的ob_refcnt就會(huì)增加,引用它的對(duì)象被刪除時(shí)則減少。一旦對(duì)象的引用計(jì)數(shù)為0,該對(duì)象立即被回收,占用空間就會(huì)被釋放。
優(yōu)點(diǎn)
- 簡(jiǎn)單易用
- 實(shí)時(shí)性好,一旦沒(méi)有引用就會(huì)被立即釋放
缺點(diǎn)
- 需要額外空間去維護(hù)引用計(jì)數(shù)
- 不能解決對(duì)象的循環(huán)引用
對(duì)象的循環(huán)引用
循環(huán)引用是指兩個(gè)對(duì)象相互引用且沒(méi)有外部變量引用其中任何一個(gè),導(dǎo)致引用鏈形成一個(gè)環(huán)。
>>> a = {} # 對(duì)象a的引用計(jì)數(shù)為1 >>> b = {} # 對(duì)象b的引用計(jì)數(shù)為1 >>> a['b'] = b # b的引用計(jì)數(shù)增加1 >>> b['a'] = a # a的引用計(jì)數(shù)增加1 >>> del a # a的引用計(jì)數(shù)減少1,最后a的引用為1 >>> del b # b的引用計(jì)數(shù)減少1,最后b的引用為1
在執(zhí)行完del操作之后,沒(méi)有任何引用指向a、b對(duì)象,但是由于這兩個(gè)對(duì)象各自包含一個(gè)對(duì)對(duì)方的引用,所以引用計(jì)數(shù)始終保持在1。
按照引用計(jì)數(shù)中內(nèi)存回收的原理,由于a和b的計(jì)數(shù)不為0,所以在使用引用計(jì)數(shù)法進(jìn)行內(nèi)存管理的時(shí)候這兩個(gè)對(duì)象不會(huì)被回收,它們會(huì)一直駐留在內(nèi)存中,造成內(nèi)存泄露。
標(biāo)記清除
標(biāo)記清除機(jī)制主要用于解決循環(huán)引用問(wèn)題。
標(biāo)記清除算法是一種基于追蹤回收(tracing GC)技術(shù)實(shí)現(xiàn)的垃圾回收算法。主要分為兩個(gè)階段:
- 標(biāo)記階段,GC會(huì)將所有的活動(dòng)對(duì)象打上標(biāo)記
- 對(duì)那些沒(méi)有打上標(biāo)記的非活動(dòng)對(duì)象進(jìn)行回收
區(qū)分活動(dòng)對(duì)象與非活動(dòng)對(duì)象
對(duì)象之間通過(guò)引用即指針連接在一起,構(gòu)成一個(gè)有向圖,對(duì)象就是這個(gè)有向圖的節(jié)點(diǎn),而引用關(guān)系構(gòu)成這個(gè)有向圖的邊。從根對(duì)象(root object)出發(fā),沿著有向邊遍歷對(duì)象,可達(dá)的對(duì)象會(huì)被標(biāo)記為活動(dòng)對(duì)象,不可達(dá)的對(duì)象就是要被清除的非活動(dòng)對(duì)象。
根對(duì)象一般是全局變量、調(diào)用棧、寄存器等。
適用范圍
標(biāo)記清除算法作為Python輔助的垃圾收集技術(shù),主要處理的是容器對(duì)象,因?yàn)閷?duì)于字符串、數(shù)值對(duì)象等,不可能造成循環(huán)引用的問(wèn)題,Python會(huì)使用一個(gè)雙向鏈表將這些容器對(duì)象組織起來(lái)。
對(duì)于標(biāo)記清除算法來(lái)說(shuō),有一個(gè)比較明顯的缺點(diǎn):為了清除非活動(dòng)對(duì)象,需要掃描整個(gè)堆內(nèi)存,哪怕只剩下小部分活動(dòng)對(duì)象也需要掃描所有對(duì)象。
分代回收
分代回收是一種以空間換時(shí)間的操作方式,建立在標(biāo)記清除技術(shù)的基礎(chǔ)之上,也是Python輔助的垃圾收集技術(shù),主要用于處理容器對(duì)象。
Python會(huì)將內(nèi)存根據(jù)對(duì)象的存活時(shí)間劃分為不同的集合,每個(gè)集合稱為一個(gè)代,主要會(huì)被分為3代:年輕代。中年代和老年代,它們會(huì)對(duì)應(yīng)3個(gè)鏈表,對(duì)應(yīng)的垃圾收集頻率隨著對(duì)象存活時(shí)間的增大而減小。
新創(chuàng)建的對(duì)象都會(huì)被分配在年輕代,當(dāng)年輕代鏈表總數(shù)達(dá)到上限時(shí),會(huì)觸發(fā)Python的垃圾回收機(jī)制,對(duì)可回收對(duì)象進(jìn)行回收,而那些不可回收的對(duì)象會(huì)被移到中年代去。依此類推,老年代對(duì)象是存活時(shí)間最久的對(duì)象,甚至有可能存活在整個(gè)系統(tǒng)的生命周期內(nèi)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
