python有向图_Python 中的垃圾回收机制
一、概述
python采用的是引用計(jì)數(shù)機(jī)制為主,標(biāo)記-清除和分代收集(隔代回收)兩種機(jī)制為輔的策略。
現(xiàn)在的高級(jí)語(yǔ)言如java,c#等,都采用了垃圾收集機(jī)制,而不再是c,c++里用戶(hù)自己管理維護(hù)內(nèi)存的方式。自己管理內(nèi)存極其自由,可以任意申請(qǐng)內(nèi)存,但如同一把雙刃劍,為大量?jī)?nèi)存泄露,懸空指針等bug埋下隱患。對(duì)于一個(gè)字符串、列表、類(lèi)甚至數(shù)值都是對(duì)象,且定位簡(jiǎn)單易用的語(yǔ)言,自然不會(huì)讓用戶(hù)去處理如何分配回收內(nèi)存的問(wèn)題。
python里也同java一樣采用了垃圾收集機(jī)制,不過(guò)不一樣的是:
python采用的是引用計(jì)數(shù)機(jī)制為主,標(biāo)記-清除和分代收集(隔代回收)兩種機(jī)制為輔的策略。
二、引用計(jì)數(shù)機(jī)制
引用計(jì)數(shù)法機(jī)制的原理是:每個(gè)對(duì)象維護(hù)一個(gè)ob_ref字段,用來(lái)記錄該對(duì)象當(dāng)前被引用的次數(shù),每當(dāng)新的引用指向該對(duì)象時(shí),它的引用計(jì)數(shù)ob_ref加1,每當(dāng)該對(duì)象的引用失效時(shí)計(jì)數(shù)ob_ref減1,一旦對(duì)象的引用計(jì)數(shù)為0,該對(duì)象立即被回收,對(duì)象占用的內(nèi)存空間將被釋放。它的缺點(diǎn)是需要額外的空間維護(hù)引用計(jì)數(shù),這個(gè)問(wèn)題是其次的,不過(guò)最主要的問(wèn)題是它不能解決對(duì)象的“循環(huán)引用”,因此,也有很多語(yǔ)言比如Java并沒(méi)有采用該算法做來(lái)垃圾的收集機(jī)制。python里每一個(gè)東西都是對(duì)象,它們的核心就是一個(gè)結(jié)構(gòu)體:PyObject
PyObject是每個(gè)對(duì)象必有的內(nèi)容,其中ob_refcnt就是做為引用計(jì)數(shù)。當(dāng)一個(gè)對(duì)象有新的引用時(shí),它的ob_refcnt就會(huì)增加,當(dāng)引用它的對(duì)象被刪除,它的ob_refcnt就會(huì)減少
引用計(jì)數(shù)為0時(shí),該對(duì)象生命就結(jié)束了。
引用計(jì)數(shù)機(jī)制的優(yōu)點(diǎn):
1、簡(jiǎn)單
2、實(shí)時(shí)性:一旦沒(méi)有引用,內(nèi)存就直接釋放了,不用像其他機(jī)制得等到特定時(shí)機(jī)。實(shí)時(shí)性還帶來(lái)一個(gè)好處:處理回收內(nèi)存的時(shí)間分?jǐn)偟搅似綍r(shí)。
引用計(jì)數(shù)機(jī)制的缺點(diǎn):
1、維護(hù)引用計(jì)數(shù)消耗資源
2、循環(huán)引用
案例:
import sys class A():def __init__(self):'''初始化對(duì)象'''print('object born id:%s' %str(hex(id(self))))def f1():'''循環(huán)引用變量與刪除變量'''while True:c1=A()del c1def func(c):print('obejct refcount is: ',sys.getrefcount(c)) #getrefcount()方法用于返回對(duì)象的引用計(jì)數(shù)if __name__ == '__main__':#生成對(duì)象a=A()func(a)#增加引用b=afunc(a)#銷(xiāo)毀引用對(duì)象bdel bfunc(a) #結(jié)果 object born id:0x19f5ecb9320 obejct refcount is: 4 obejct refcount is: 5 obejct refcount is: 4導(dǎo)致引用計(jì)數(shù)+1的情況
- 對(duì)象被創(chuàng)建,例如a=23
- 對(duì)象被引用,例如b=a
- 對(duì)象被作為參數(shù),傳入到一個(gè)函數(shù)中,例如func(a)
- 對(duì)象作為一個(gè)元素,存儲(chǔ)在容器中,例如list1=[a,a]
導(dǎo)致引用計(jì)數(shù)-1的情況
- 對(duì)象的別名被顯式銷(xiāo)毀,例如del a
- 對(duì)象的別名被賦予新的對(duì)象,例如a=24
- 一個(gè)對(duì)象離開(kāi)它的作用域,例如:func函數(shù)執(zhí)行完畢時(shí),func函數(shù)中的局部變量(全局變量不會(huì))
- 對(duì)象所在的容器被銷(xiāo)毀,或從容器中刪除對(duì)象
循環(huán)引用導(dǎo)致內(nèi)存泄露
def f2():'''循環(huán)引用'''while True:c1=A()c2=A()c1.t=c2c2.t=c1del c1del c2- 創(chuàng)建了c1,c2后,這兩個(gè)對(duì)象的引用計(jì)數(shù)都是1,執(zhí)行c1.t=c2和c2.t=c1后,引用計(jì)數(shù)變成2.
- 在del c1后,內(nèi)存c1的對(duì)象的引用計(jì)數(shù)變?yōu)?,由于不是為0,所以c1的對(duì)象不會(huì)被銷(xiāo)毀,同理,在del c2后也是一樣的。
- 雖然它們兩個(gè)的對(duì)象都是可以被銷(xiāo)毀的,但是由于循環(huán)引用,導(dǎo)致垃圾回收器都不會(huì)回收它們,所以就會(huì)導(dǎo)致內(nèi)存泄露。
分代回收
- 分代回收是一種以空間換時(shí)間的操作方式,Python將內(nèi)存根據(jù)對(duì)象的存活時(shí)間劃分為不同的集合,每個(gè)集合稱(chēng)為一個(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ì)被移到中年代去,依此類(lèi)推,老年代中的對(duì)象是存活時(shí)間最久的對(duì)象,甚至是存活于整個(gè)系統(tǒng)的生命周期內(nèi)。
- 同時(shí),分代回收是建立在標(biāo)記清除技術(shù)基礎(chǔ)之上。分代回收同樣作為Python的輔助垃圾收集技術(shù)處理那些容器對(duì)象
垃圾回收
有三種情況會(huì)觸發(fā)垃圾回收:
gc模塊
gc模塊提供一個(gè)接口給開(kāi)發(fā)者設(shè)置垃圾回收的選項(xiàng)。上面說(shuō)到,采用引用計(jì)數(shù)的方法管理內(nèi)存的一個(gè)缺陷是循環(huán)引用,而gc模塊的一個(gè)主要功能就是解決循環(huán)引用的問(wèn)題。
常用函數(shù):
顯式進(jìn)行垃圾回收,可以輸入?yún)?shù),0代表只檢查第一代的對(duì)象,1代表檢查一,二代的對(duì)象,2代表檢查一,二,三代的對(duì)象,如果不傳參數(shù),執(zhí)行一個(gè)full collection,也就是等于傳2。返回不可達(dá)(unreachable objects)對(duì)象的數(shù)目。
設(shè)置自動(dòng)執(zhí)行垃圾回收的頻率。
gc實(shí)踐案例
def f3():'''循環(huán)引用'''while True:c1=A()c2=A()c1.t=c2c2.t=c1del c1del c2#增加垃圾回收機(jī)制print(gc.garbage)print(gc.collect())print(gc.garbage)time.sleep(10) #結(jié)果 object born id:0x21d1a5dc470 object born id:0x21d1a5dc9e8 [] 4 gc: collectable <A 0x0000021D1A5DC470> [<__main__.A object at 0x0000021D1A5DC470>, <__main__.A object at 0x0000021D1A5DC9E8>, {'t': <__main__.A object at 0x0000021D1A5DC9E8>}, {'t': <__main__.A object at 0x0000021D1A5DC470>}] gc: collectable <A 0x0000021D1A5DC9E8> gc: collectable <dict 0x0000021D1A156C88> gc: collectable <dict 0x0000021D1A5CABC8>gc模塊的自動(dòng)垃圾回收機(jī)制
必須要import gc模塊,并且is_enable()=True才會(huì)啟動(dòng)自動(dòng)垃圾回收。
這個(gè)機(jī)制的主要作用就是發(fā)現(xiàn)并處理不可達(dá)的垃圾對(duì)象。
在Python中,采用分代收集的方法。把對(duì)象分為三代,一開(kāi)始,對(duì)象在創(chuàng)建的時(shí)候,放在一代中,如果在一次一代的垃圾檢查中,該對(duì)象存活下來(lái),就會(huì)被放到二代中,同理在一次二代的垃圾檢查中,該對(duì)象存活下來(lái),就會(huì)被放到三代中。
gc模塊里面會(huì)有一個(gè)長(zhǎng)度為3的列表的計(jì)數(shù)器,可以通過(guò)gc.get_count()獲取。
def f4():'''垃圾自動(dòng)回收'''print(gc.get_count())a=A()print(gc.get_count())del aprint(gc.get_count()) #結(jié)果 (621, 10, 0) object born id:0x2ca32a8c588 (624, 10, 0) (623, 10, 0)- 621指距離上一次一代垃圾檢查,Python分配內(nèi)存的數(shù)目減去釋放內(nèi)存的數(shù)目,注意:是內(nèi)存分配,而不是引用計(jì)數(shù)的增加。
- 10指距離上一次二代垃圾檢查,一代垃圾檢查的次數(shù)。
- 0是指距離上一次三代垃圾檢查,二代垃圾檢查的次數(shù)。
自動(dòng)回收閾值
gc模快有一個(gè)自動(dòng)垃圾回收的閥值,即通過(guò)gc.get_threshold函數(shù)獲取到的長(zhǎng)度為3的元組,例如(700,10,10)
每一次計(jì)數(shù)器的增加,gc模塊就會(huì)檢查增加后的計(jì)數(shù)是否達(dá)到閥值的數(shù)目,如果是,就會(huì)執(zhí)行對(duì)應(yīng)的代數(shù)的垃圾檢查,然后重置計(jì)數(shù)器
注意:
如果循環(huán)引用中,兩個(gè)對(duì)象都定義了__del__方法,gc模塊不會(huì)銷(xiāo)毀這些不可達(dá)對(duì)象,因?yàn)間c模塊不知道應(yīng)該先調(diào)用哪個(gè)對(duì)象的__del__方法,所以為了安全起見(jiàn),gc模塊會(huì)把對(duì)象放到gc.garbage中,但是不會(huì)銷(xiāo)毀對(duì)象。
標(biāo)記清除
標(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è)有向圖,對(duì)象構(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)用棧、寄存器。 mark-sweepg 在上圖中,我們把小黑圈視為全局變量,也就是把它作為root object,從小黑圈出發(fā),對(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)記清除算法作為Python的輔助垃圾收集技術(shù)主要處理的是一些容器對(duì)象,比如list、dict、tuple,instance等,因?yàn)閷?duì)于字符串、數(shù)值對(duì)象是不可能造成循環(huán)引用問(wèn)題。Python使用一個(gè)雙向鏈表將這些容器對(duì)象組織起來(lái)。不過(guò),這種簡(jiǎn)單粗暴的標(biāo)記清除算法也有明顯的缺點(diǎn):清除非活動(dòng)的對(duì)象前它必須順序掃描整個(gè)堆內(nèi)存,哪怕只剩下小部分活動(dòng)對(duì)象也要掃描所有對(duì)象。
總結(jié)
以上是生活随笔為你收集整理的python有向图_Python 中的垃圾回收机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python虚拟环境的安装和配置_基于v
- 下一篇: python office自动化_Pyt