日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

python有向图_Python 中的垃圾回收机制

發(fā)布時(shí)間:2025/3/20 python 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python有向图_Python 中的垃圾回收机制 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、概述

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ā)垃圾回收:

  • 調(diào)用gc.collect(),需要先導(dǎo)入gc模塊。
  • 當(dāng)gc模塊的計(jì)數(shù)器達(dá)到閾值的時(shí)候。
  • 程序退出的時(shí)候。
  • gc模塊

    gc模塊提供一個(gè)接口給開(kāi)發(fā)者設(shè)置垃圾回收的選項(xiàng)。上面說(shuō)到,采用引用計(jì)數(shù)的方法管理內(nèi)存的一個(gè)缺陷是循環(huán)引用,而gc模塊的一個(gè)主要功能就是解決循環(huán)引用的問(wèn)題。

    常用函數(shù):

  • gc.set_debug(flags) 設(shè)置gc的debug日志,一般設(shè)置為gc.DEBUG_LEAK
  • gc.collect([generation])
    顯式進(jìn)行垃圾回收,可以輸入?yún)?shù),0代表只檢查第一代的對(duì)象,1代表檢查一,二代的對(duì)象,2代表檢查一,二,三代的對(duì)象,如果不傳參數(shù),執(zhí)行一個(gè)full collection,也就是等于傳2。返回不可達(dá)(unreachable objects)對(duì)象的數(shù)目。
  • gc.set_threshold(threshold0[, threshold1[, threshold2])
    設(shè)置自動(dòng)執(zhí)行垃圾回收的頻率。
  • gc.get_count() 獲取當(dāng)前自動(dòng)執(zhí)行垃圾回收的計(jì)數(shù)器,返回一個(gè)長(zhǎng)度為3的列表
  • 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)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。