導航:首頁 > 源碼編譯 > 標記回收演算法python

標記回收演算法python

發布時間:2023-01-02 03:39:17

① 面試必備 - python 垃圾回收機制

眾所周知,Python 是一門面向對象語言,在 Python 的世界一切皆對象。所以一切變數的本質都是對象的一個指針而已。

Python 運行過程中會不停的創建各種變數,而這些變數是需要存儲在內存中的,隨著程序的不斷運行,變數數量越來越多,所佔用的空間勢必越來越大,如果對變數所佔用的內存空間管理不當的話,那麼肯定會出現 out of memory。程序大概率會被異常終止。

因此,對於內存空間的有效合理管理變得尤為重要,那麼 Python 是怎麼解決這個問題的呢。其實很簡單,對不不可能再使用到的內存進行回收即可,像 C 語言中需要程序員手動釋放內存就是這個道理。但問題是如何確定哪些內存不再會被使用到呢?這就是我們今天要說的垃圾回收了。

目前垃圾回收比較通用的解決辦法有三種,引用計數,標記清除以及分代回收。

引用計數也是一種最直觀,最簡單的垃圾收集技術。在 Python 中,大多數對象的生命周期都是通過對象的引用計數來管理的。其原理非常簡單,我們為每個對象維護一個 ref 的欄位用來記錄對象被引用的次數,每當對象被創建或者被引用時將該對象的引用次數加一,當對象的引用被銷毀時該對象的引用次數減一,當對象的引用次數減到零時說明程序中已經沒有任何對象持有該對象的引用,換言之就是在以後的程序運行中不會再次使用到該對象了,那麼其所佔用的空間也就可以被釋放了了。

我們來看看下面的例子。

函數 print_memory_info 用來獲取程序佔用的內存空間大小,在 foo 函數中創建一個包含一百萬個整數的列表。從列印結果我們可以看出,創建完列表之後程序耗用的內存空間上升到了 55 MB。而當函數 foo 調用完畢之後內存消耗又恢復正常。

這是因為我們在函數 foo 中創建的 list 變數是局部變數,其作用域是當前函數內部,一旦函數執行完畢,局部變數的引用會被自動銷毀,即其引用次數會變為零,所佔用的內存空間也會被回收。

為了驗證我們的想法,我們對函數 foo 稍加改造。代碼如下:

稍加改造之後,即使 foo 函數調用結束其所消耗的內存也未被釋放。

主要是因為我們將函數 foo 內部產生的列表返回並在主程序中接收之後,這樣就會導致該列表的引用依然存在,該對象後續仍有可能被使用到,垃圾回收便不會回收該對象。

那麼,什麼時候對象的引用次數才會增加呢。下面四種情況都會導致對象引用次數加一。

同理,對象引用次數減一的情況也有四種。

引用計數看起來非常簡單,實現起來也不復雜,只需要維護一個欄位保存對象被引用的次數即可,那麼是不是就代表這種演算法沒有缺點了呢。實則不然,我們知道引用次數為零的對象所佔用的內存空間肯定是需要被回收的。那引用次數不為零的對象呢,是不是就一定不能回收呢?

我們來看看下面的例子,只是對函數 foo 進行了改造,其餘未做更改。

我們看到,在函數 foo 內部生成了兩個列表 list_a 和 list_b,然後將兩個列表分別添加到另外一個中。由結果可以看出,即使 foo 函數結束之後其所佔用的內存空間依然未被釋放。這是因為對於 list_a 和 list_b 來說雖然沒有被任何外部對象引用,但因為二者之間交叉引用,以至於每個對象的引用計數都不為零,這也就造成了其所佔用的空間永遠不會被回收的尷尬局面。這個缺點是致命的。

為了解決交叉引用的問題,Python 引入了標記清除演算法和分代回收演算法。

顯然,可以包含其他對象引用的容器對象都有可能產生交叉引用問題,而標記清除演算法就是為了解決交叉引用的問題的。

標記清除演算法是一種基於對象可達性分析的回收演算法,該演算法分為兩個步驟,分別是標記和清除。標記階段,將所有活動對象進行標記,清除階段將所有未進行標記的對象進行回收即可。那麼現在的為問題變為了 GC 是如何判定哪些是活動對象的?

事實上 GC 會從根結點出發,與根結點直接相連或者間接相連的對象我們將其標記為活動對象(該對象可達),之後進行回收階段,將未標記的對象(不可達對象)進行清除。前面所說的根結點可以是全局變數,也可以是調用棧。

標記清除演算法主要用來處理一些容器對象,雖說該方法完全可以做到不誤殺不遺漏,但 GC 時必須掃描整個堆內存,即使只有少量的非可達對象需要回收也需要掃描全部對象。這是一種巨大的性能浪費。

由於標記清除演算法需要掃描整個堆的所有對象導致其性能有所損耗,而且當可以回收的對象越少時性能損耗越高。因此 Python 引入了分代回收演算法,將系統中存活時間不同的對象劃分到不同的內存區域,共三代,分別是 0 代,1 代 和 2 代。新生成的對象是 0 代,經過一次垃圾回收之後,還存活的對象將會升級到 1 代,以此類推,2 代中的對象是存活最久的對象。

那麼什麼時候觸發進行垃圾回收演算法呢。事實上隨著程序的運行會不斷的創建新的對象,同時也會因為引用計數為零而銷毀大部分對象,Python 會保持對這些對象的跟蹤,由於交叉引用的存在,以及程序中使用了長時間存活的對象,這就造成了新生成的對象的數量會大於被回收的對象數量,一旦二者之間的差值達到某個閾值就會啟動垃圾回收機制,使用標記清除演算法將死亡對象進行清除,同時將存活對象移動到 1 代。 以此類推,當二者的差值再次達到閾值時又觸發垃圾回收機制,將存活對象移動到 2 代。

這樣通過對不同代的閾值做不同的設置,就可以做到在不同代使用不同的時間間隔進行垃圾回收,以追求性能最大。

事實上,所有的程序都有一個相識的現象,那就是大部分的對象生存周期都是相當短的,只有少量對象生命周期比較長,甚至會常駐內存,從程序開始運行持續到程序結束。而通過分代回收演算法,做到了針對不同的區域採取不同的回收頻率,節約了大量的計算從而提高 Python 的性能。

除了上面所說的差值達到一定閾值會觸發垃圾回收之外,我們還可以顯示的調用 gc.collect() 來觸發垃圾回收,最後當程序退出時也會進行垃圾回收。

本文介紹了 Python 的垃圾回收機制,垃圾回收是 Python 自帶的功能,並不需要程序員去手動管理內存。

其中引用計數法是最簡單直接的,但是需要維護一個欄位且針對交叉引用無能為力。

標記清除演算法主要是為了解決引用計數的交叉引用問題,該演算法的缺點就是需要掃描整個堆的所有對象,有點浪費性能。

而分代回收演算法的引入則完美解決了標記清除演算法需要掃描整個堆對象的性能浪費問題。該演算法也是建立在標記清除基礎之上的。

最後我們可以通過 gc.collect() 手動觸發 GC 的操作。

題外話,如果你看過 JVM 的垃圾回收演算法之後會發現 Python 的垃圾回收演算法與其是如出一轍的,事實再次證明,程序語言設計時是會相互參考的。

② python垃圾回收機制(超詳細)

概述:引用計數為主,標記清除,分代回收為輔

1引用計數

python程序中創建的所有的對象都是放在一個雙向環狀循環鏈表refchain上的

如下對象被創建時,在C語言底層實際結構

name='string'

c語言內底部創建成 [上一個對象,下一個對象,類型,引用個數]

age=18

c語言內底部創建成[上一個對象,下一個對象,類型,引用個數,val=18]

hobby=['籃球', '擼鐵',『玩』]

c語言內底部創建成[上一個對象,下一個對象,類型,引用個數,item=元素, 元素個數]

當python程序運行時,會根據數據類型的不同找到其對應的結構體,根據結構體中的欄位來進行創建相關的數據,然後將對象添加到refchain雙向鏈表中

每個對象中有ob_refcnt就是應用計數器,默認為1,當有其他變數引用對象時,引用計數器就會+1

當引用計數器為0時,意味著沒人使用這個對象了,這個對象就是垃圾,就會回收

回收步驟 :1對象從refchain鏈表移除  2將對象銷毀,內存回收

2 標記清除

為什麼要標記清除 :為了解決引用計數器循環引用的不足,循環引用可能導致內存泄漏

實現:在python的底層,再維護一個鏈表,鏈表中專門放那些可能存在循環引用的對象(list/tuple/dict/set)

在python內部,某種情況下觸發,回去掃描可能存在循環引用鏈表中的每個元素,檢查是否是循環引用,如果有,則讓雙方的引用計數器-1,如果是0,則垃圾回收

3 分代回收

為什麼要分代回收: 不知道什麼情況下觸發掃描,可能存在循環引用的鏈表掃描代價大,每次掃描很久

將可能存在循環引用的對象維護成3個鏈表

0代:0代中對象個數達到700個掃描一次

1代:0代掃描10次,則1代掃描1次

2代:1代掃描10次,則2代掃描1次

過程:當我們創建了一個對象a=1,這個對象只會加到refchain鏈表中,而當我們創建了一個可能存在循環引用的對象b=[]一個列表時,這個對象不但會加到refchain鏈表中,還會加到分帶回收的0代鏈表中,當0代鏈表中對象達到700個,GC開始掃描,如果是循環引用,那就自減1,減完以後,如果是垃圾,那就自動回收,如果不是垃圾,那就將這些對象升級到1代鏈表中,就這樣掃描一遍,此時0代鏈表也會記錄自己掃描了1次,等到下次0代鏈表的對象又達到了700個,繼續上述步驟,就這樣執行了10次,才會觸發執行掃描1代鏈表,1代鏈表和2代鏈表中的操作和0代中一樣。

4 小結(面試可以這么說)

在python中,維護了一個refchain的雙向循環環狀鏈表,這個鏈表中存儲程序創建的所有對象,每種類型的對象中都有一個0b_refcnt引用計數器的值,默認為1,當引用計數器變為0時會進行垃圾回收(對象銷毀,refchain中移除)

但是,在python 中,對於那些可以有多個元素組成的對象可能會存在循環引用的問題,為了解決這個問題,python又引入了標記清除和分代回收,在其內部維護了四個鏈表

refchain 

0代  700個對象觸發

1代  0代十次執行一次1代

2代 1代十次執行一次2代

當 每個鏈表達到閾值時,就會觸發掃描鏈表進行標記清除操作,有循環則各自-1,為0時,直接回收,銷毀,清除

But, 在上面的垃圾回收機制的步驟中,python提供了優化機制

緩存

小整數對象池

為了避免重復創建和銷毀一些常用對象,維護了一個小整數對象池

-5~257的地址內存是一定的,這些對象是pyhton事先幫我們創建好了

free_list(會有大小限制)

當一個對象的引用計數為0時,按理說應該回收,但是python沒有回收,而是把這個對象放到了一個free_list中當緩存,以後再去使用時,不在重新開辟內存,而是直接使用free_list

比如現在一個對象V=3.14 ,我現在把他del V, 代表引用計數為0 了,但是這塊地址我不會回收,而是放到free_list中,然後我又創建了一個新的對象v1=999,這個對象不會開辟一塊新內存,而是直接從free_list中去獲取對象,然後把對象內部的數據進行初始化成999,再放到refchain中去,需要注意的是,free_list有大小限制,如果free_list鏈表滿了,當一個對象的引用計數為0時,會直接回收這塊地址,而不會放到free_list中進行緩存

float: 維護了free_list長度為100

int:不是基於free_list, 而是維護一個small_list保持常見的數據(小數據池),重復使用不會開辟新的內存

str: 內存將所有的ascii字元緩存起來,以後使用的時候不會反復創建

list: 維護了free_list長度為80

tuple:根據元素個數來維護free_list長度

dict:維護了free_list長度為80

③ python中的變數與垃圾回收

python中的變數和java中的變數本質是不一樣的,python中的變數實質上是一個指針(指針的大小固定的)

is可以用來判斷id是否相等

對於這種賦值,雖然所賦值是相同的,但是他們的id不同,即他們是不同的對象,a is b 即為false ,但是有個特例: a = 1 b = 1 時他們的id相同。其實這是python內部的優化機制,對於小整數和小的字元串來說,python在前邊定義一個對象時,下次在遇到時會直接調用前邊生成的對象,而不會去重新申請一個。

他們的對象內存地址不一樣,但是,a和b里的值是相等的,這是由於a和b都為list,而list里有內置的魔法函數 eq 通過 eq 魔法函數可以判斷里邊兩個的值是否相同,若相同則返回True

python中垃圾回收的演算法回收的演算法是採用引用計數,當程序中有一個變數引用該python對象時,python會自動保證該對象引用計數為1;當程序中有兩個變數引用該python對象時,python會自動保證該對象計數器為2, 以此類推,當一個對象的引用計數器變為0 時,則說明程序中不再有變數對其進行引用,因此python就會回收該對象。

大多數情況,python的ARC都能准確,高效的回收系統中的每一個對象。但如果系統中出現循環引用時,比如a對象持有一個實例變數引用對象b,而b對象又持有一個實例變數引用對象a,此時 兩個對象的計數器都為1, 而實際上python不再需要這兩個對象,也沒有程序在引用他們,系統回收他們時python的垃圾回收器就沒有那兒快,要等到專門的循環垃圾回收器(Cyclic Garbage Collector)來檢測並回收這種引用循環

當一個對象被垃圾回收式,python就會自動調用該對象的 del 方法

當沒有注釋掉x = im時, item對象被兩個變數所引用,所以在執行完del im時並不會去回收item對象,所以先輸出--------,當程序完全執行完成後,引用item的對象的變數被釋放,然後系統便會執行 del 方法,回收item對象。

當 x = im被注釋後,只有一個變數去引用item對象,所以在執行完後程序變回去調用 del 方法,回收item對象,然後在繼續向下執行 輸出-----

④ python的回收機制是什麼

Python中的垃圾回收機制總體上有三種,

引用計數

Python語言默認採用的垃圾收集機制是『引用計數法 Reference Counting』,該演算法最早George E. Collins在1960的時候首次提出,50年後的今天,該演算法依然被很多編程語言使用,『引用計數法』的原理是:每個對象維護一個ob_ref欄位,用來記錄該對象當前被引用的次數,每當新的引用指向該對象時,它的引用計數ob_ref加1,每當該對象的引用失效時計數ob_ref減1,一旦對象的引用計數為0,該對象立即被回收,對象佔用的內存空間將被釋放。它的缺點是需要額外的空間維護引用計數,這個問題是其次的,不過最主要的問題是它不能解決對象的「循環引用」,因此,也有很多語言比如Java並沒有採用該演算法做來垃圾的收集機制。

在上圖中,我們把小黑圈視為全局變數,也就是把它作為root object,從小黑圈出發,對象1可直達,那麼它將被標記,對象2、3可間接到達也會被標記,而4和5不可達,那麼1、2、3就是活動對象,4和5是非活動對象會被GC回收。

標記清除演算法作為Python的輔助垃圾收集技術主要處理的是一些容器對象,比如list、dict、tuple,instance等,因為對於字元串、數值對象是不可能造成循環引用問題。Python使用一個雙向鏈表將這些容器對象組織起來。不過,這種簡單粗暴的標記清除演算法也有明顯的缺點:清除非活動的對象前它必須順序掃描整個堆內存,哪怕只剩下小部分活動對象也要掃描所有對象。

分代回收

分代回收是一種以空間換時間的操作方式,Python將內存根據對象的存活時間劃分為不同的集合,每個集合稱為一個代,Python將內存分為了3「代」,分別為年輕代(第0代)、中年代(第1代)、老年代(第2代),他們對應的是3個鏈表,它們的垃圾收集頻率與對象的存活時間的增大而減小。新創建的對象都會分配在年輕代,年輕代鏈表的總數達到上限時,Python垃圾收集機制就會被觸發,把那些可以被回收的對象回收掉,而那些不會回收的對象就會被移到中年代去,依此類推,老年代中的對象是存活時間最久的對象,甚至是存活於整個系統的生命周期內。同時,分代回收是建立在標記清除技術基礎之上。分代回收同樣作為Python的輔助垃圾收集技術處理那些容器對象。

Python垃圾回收機制--完美講解! 東皇Amrzs

Python中的垃圾回收機制

⑤ python 怎麼在空閑時用gc回收

importgc
importsys

classCGcLeak(object):
def__init__(self):
self._text='#'*10

def__del__(self):
pass

defmake_circle_ref():
_gcleak=CGcLeak()
print"_gcleakrefcount0:%d"%(sys.getrefcount(_gcleak))
del_gcleak
try:
print"_gcleakrefcount1:%d"%(sys.getrefcount(_gcleak))
exceptUnboundLocalError:#本地變數xxx引用前沒定義
print"_gcleakisinvalid!"
deftest_gcleak():
gc.enable()#設置垃圾回收器調試標志
gc.set_debug(gc.DEBUG_COLLECTABLE|gc.DEBUG_UNCOLLECTABLE|gc.DEBUG_INSTANCES|gc.DEBUG_OBJECTS)

print"beginleaktest..."
make_circle_ref()

print" begincollect..."
_unreachable=gc.collect()
print"unreachableobjectnum:%d"%(_unreachable)
print"garbageobjectnum:%d"%(len(gc.garbage))#gc.garbage是一個list對象,列表項是垃圾收集器發現的不可達(即垃圾對象)、但又不能釋放(不可回收)的對象,通常gc.garbage中的對象是引用對象還中的對象。因Python不知用什麼順序來調用對象的__del__函數,導致對象始終存活在gc.garbage中,造成內存泄露if__name__=="__main__":test_gcleak()。如果知道一個安全次序,那麼就可以打破引用煥,再執行delgc.garbage[:]從而清空垃圾對象列表
if__name__=="__main__":
test_gcleak()

⑥ Python的垃圾回收機制(garbage collection)是什麼

這里能說的很多。你應該提到下面幾個主要的點:

Python在內存中存儲了每個對象的引用計數(reference count)。如果計數值變成0,那麼相應的對象就會小時,分配給該對象的內存就會釋放出來用作他用。

偶爾也會出現引用循環(reference cycle)。垃圾回收器會定時尋找這個循環,並將其回收。舉個例子,假設有兩個對象o1和o2,而且符合o1.x == o2和o2.x == o1這兩個條件。如果o1和o2沒有其他代碼引用,那麼它們就不應該繼續存在。但它們的引用計數都是1。來自三人行慕課

Python中使用了某些啟發式演算法(heuristics)來加速垃圾回收。例如,越晚創建的對象更有可能被回收。對象被創建之後,垃圾回收器會分配它們所屬的代(generation)。每個對象都會被分配一個代,而被分配更年輕代的對象是優先被處理的。

⑦ python的回收機制是什麼

Python中有自動內存回收機制,一般學Python,很少需要關注垃圾回收機制
主要兩個方式,一個是引用計數,另一個是標記清除
第一種方式簡單實時,缺點是保存對象引用次數會佔用內存空間,每次執行語句都可能更新引用次數,不能處理循環引用的情況
第二種方式主要是用來處理循環引用,向容器類型對象,比如list dict,很容易出現循環引用,
處理過程是首先將所有容器對象放到一個雙向鏈表中,循環遍歷鏈表,如果被本列表內的對象引用,自身的被引用次數減一,如果被引用數為零,則觸發回收條件,會被回收的對象升級為一代

⑧ python的內存管理機制

論壇

活動

招聘

專題

打開CSDN APP
Copyright © 1999-2020, CSDN.NET, All Rights Reserved

登錄

XCCS_澍
關注
Python 的內存管理機制及調優手段? 原創
2018-08-05 06:50:53

XCCS_澍

碼齡7年

關注
內存管理機制:引用計數、垃圾回收、內存池。
一、引用計數:
    引用計數是一種非常高效的內存管理手段, 當一個 Python 對象被引用時其引用計數增加 1, 當其不再被一個變數引用時則計數減 1. 當引用計數等於 0 時對象被刪除。
二、垃圾回收 :
1. 引用計數
      引用計數也是一種垃圾收集機制,而且也是一種最直觀,最簡單的垃圾收集技術。當 Python 的某個對象的引用計數降為 0 時,說明沒有任何引用指向該對象,該對象就成為要被回收的垃圾了。比如某個新建對象,它被分配給某個引用,對象的引用計數變為 1。如果引用被刪除,對象的引用計數為 0,那麼該對象就可以被垃圾回收。不過如果出現循環引用的話,引用計數機制就不再起有效的作用了
2. 標記清除
     如果兩個對象的引用計數都為 1,但是僅僅存在他們之間的循環引用,那麼這兩個對象都是需要被回收的,也就是說,它們的引用計數雖然表現為非 0,但實際上有效的引用計數為 0。所以先將循環引用摘掉,就會得出這兩個對象的有效計數。
3. 分代回收
     從前面「標記-清除」這樣的垃圾收集機制來看,這種垃圾收集機制所帶來的額外操作實際上與系統中總的內存塊的數量是相關的,當需要回收的內存塊越多時,垃圾檢測帶來的額外操作就越多,而垃圾回收帶來的額外操作就越少;反之,當需回收的內存塊越少時,垃圾檢測就將比垃圾回收帶來更少的額外操作。

⑨ java有哪些垃圾回收演算法

常用的垃圾回收演算法有:
(1).引用計數演算法:
給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器都為0的對象就是不再被使用的,垃圾收集器將回收該對象使用的內存。
引用計數演算法實現簡單,效率很高,微軟的COM技術、ActionScript、Python等都使用了引用計數演算法進行內存管理,但是引用計數演算法對於對象之間相互循環引用問題難以解決,因此java並沒有使用引用計數演算法。
(2).根搜索演算法:
通過一系列的名為「GC Root」的對象作為起點,從這些節點向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Root沒有任何引用鏈相連時,則該對象不可達,該對象是不可使用的,垃圾收集器將回收其所佔的內存。
主流的商用程序語言C#、java和Lisp都使用根搜素演算法進行內存管理。
在java語言中,可作為GC Root的對象包括以下幾種對象:
a. java虛擬機棧(棧幀中的本地變數表)中的引用的對象。
b.方法區中的類靜態屬性引用的對象。
c.方法區中的常量引用的對象。
d.本地方法棧中JNI本地方法的引用對象。
java方法區在Sun HotSpot虛擬機中被稱為永久代,很多人認為該部分的內存是不用回收的,java虛擬機規范也沒有對該部分內存的垃圾收集做規定,但是方法區中的廢棄常量和無用的類還是需要回收以保證永久代不會發生內存溢出。
判斷廢棄常量的方法:如果常量池中的某個常量沒有被任何引用所引用,則該常量是廢棄常量。
判斷無用的類:
(1).該類的所有實例都已經被回收,即java堆中不存在該類的實例對象。
(2).載入該類的類載入器已經被回收。
(3).該類所對應的java.lang.Class對象沒有任何地方被引用,無法在任何地方通過反射機制訪問該類的方法。
Java中常用的垃圾收集演算法:
(1).標記-清除演算法:
最基礎的垃圾收集演算法,演算法分為「標記」和「清除」兩個階段:首先標記出所有需要回收的對象,在標記完成之後統一回收掉所有被標記的對象。
標記-清除演算法的缺點有兩個:首先,效率問題,標記和清除效率都不高。其次,標記清除之後會產生大量的不連續的內存碎片,空間碎片太多會導致當程序需要為較大對象分配內存時無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。
(2).復制演算法:
將可用內存按容量分成大小相等的兩塊,每次只使用其中一塊,當這塊內存使用完了,就將還存活的對象復制到另一塊內存上去,然後把使用過的內存空間一次清理掉。這樣使得每次都是對其中一塊內存進行回收,內存分配時不用考慮內存碎片等復雜情況,只需要移動堆頂指針,按順序分配內存即可,實現簡單,運行高效。
復制演算法的缺點顯而易見,可使用的內存降為原來一半。
(3).標記-整理演算法:
標記-整理演算法在標記-清除演算法基礎上做了改進,標記階段是相同的標記出所有需要回收的對象,在標記完成之後不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,在移動過程中清理掉可回收的對象,這個過程叫做整理。
標記-整理演算法相比標記-清除演算法的優點是內存被整理以後不會產生大量不連續內存碎片問題。
復制演算法在對象存活率高的情況下就要執行較多的復制操作,效率將會變低,而在對象存活率高的情況下使用標記-整理演算法效率會大大提高。
(4).分代收集演算法:
根據內存中對象的存活周期不同,將內存劃分為幾塊,java的虛擬機中一般把內存劃分為新生代和年老代,當新創建對象時一般在新生代中分配內存空間,當新生代垃圾收集器回收幾次之後仍然存活的對象會被移動到年老代內存中,當大對象在新生代中無法找到足夠的連續內存時也直接在年老代中創建。

⑩ Python 中的垃圾回收機制

python採用的是 引用計數 機制為主, 標記-清除 分代收集(隔代回收) 兩種機制為輔的策略。

python里每一個東西都是對象,它們的核心就是一個結構體:PyObject

PyObject是每個對象必有的內容,其中ob_refcnt就是做為引用計數。當一個對象有新的引用時,它的ob_refcnt就會增加,當引用它的對象被刪除,它的ob_refcnt就會減少

引用計數為0時,該對象生命就結束了。

引用計數機制的優點:

1、簡單

2、實時性:一旦沒有引用,內存就直接釋放了,不用像其他機製得等到特定時機。實時性還帶來一個好處:處理回收內存的時間分攤到了平時。

引用計數機制的缺點:

1、維護引用計數消耗資源

2、循環引用

案例:

循環引用導致內存泄露

有三種情況會觸發垃圾回收:

gc模塊提供一個介面給開發者設置垃圾回收的選項。上面說到,採用引用計數的方法管理內存的一個缺陷是循環引用,而gc模塊的一個主要功能就是解決循環引用的問題。

常用函數

gc實踐案例

必須要import gc模塊,並且is_enable()=True才會啟動自動垃圾回收。
這個機制的主要作用就是發現並處理不可達的垃圾對象。

在Python中,採用分代收集的方法。把對象分為三代,一開始,對象在創建的時候,放在一代中,如果在一次一代的垃圾檢查中,該對象存活下來,就會被放到二代中,同理在一次二代的垃圾檢查中,該對象存活下來,就會被放到三代中。

gc模塊裡面會有一個長度為3的列表的計數器,可以通過 gc.get_count() 獲取。

gc模快有一個自動垃圾回收的閥值,即通過 gc.get_threshold 函數獲取到的長度為3的元組,例如 (700,10,10)
每一次計數器的增加,gc模塊就會檢查增加後的計數是否達到閥值的數目,如果是,就會執行對應的代數的垃圾檢查,然後重置計數器

注意:
如果循環引用中,兩個對象都定義了 __del__ 方法,gc模塊不會銷毀這些不可達對象,因為gc模塊不知道應該先調用哪個對象的 __del__ 方法,所以為了安全起見,gc模塊會把對象放到 gc.garbage 中,但是不會銷毀對象。

標記清除(Mark—Sweep)』演算法是一種基於追蹤回收(tracing GC)技術實現的垃圾回收演算法。它分為兩個階段:第一階段是標記階段,GC會把所有的『活動對象』打上標記,第二階段是把那些沒有標記的對象『非活動對象』進行回收。那麼GC又是如何判斷哪些是活動對象哪些是非活動對象的呢?

對象之間通過引用(指針)連在一起,構成一個有向圖,對象構成這個有向圖的節點,而引用關系構成這個有向圖的邊。從根對象(root object)出發,沿著有向邊遍歷對象,可達的(reachable)對象標記為活動對象,不可達的對象就是要被清除的非活動對象。根對象就是全局變數、調用棧、寄存器。 mark-sweepg 在上圖中,我們把小黑圈視為全局變數,也就是把它作為root object,從小黑圈出發,對象1可直達,那麼它將被標記,對象2、3可間接到達也會被標記,而4和5不可達,那麼1、2、3就是活動對象,4和5是非活動對象會被GC回收。

標記清除演算法作為Python的輔助垃圾收集技術主要處理的是一些容器對象,比如list、dict、tuple,instance等,因為對於字元串、數值對象是不可能造成循環引用問題。Python使用一個雙向鏈表將這些容器對象組織起來。不過,這種簡單粗暴的標記清除演算法也有明顯的缺點:清除非活動的對象前它必須順序掃描整個堆內存,哪怕只剩下小部分活動對象也要掃描所有對象。

閱讀全文

與標記回收演算法python相關的資料

熱點內容
什麼路由可以刷機做列印機伺服器 瀏覽:5
電腦怎麼找到雲伺服器 瀏覽:871
微信怎麼發應用app 瀏覽:776
花生殼dns伺服器地址 瀏覽:648
squad伺服器一般什麼時候人多 瀏覽:479
程序員戰門課 瀏覽:474
config保存伺服器地址 瀏覽:317
預訂網吧座位的app叫什麼 瀏覽:416
香港伺服器主機地址 瀏覽:640
網店美工pdf 瀏覽:447
一堆文件夾怎麼弄出來 瀏覽:743
博途如何編譯硬體 瀏覽:418
fortran程序pdf 瀏覽:504
電池消耗演算法 瀏覽:394
伺服器中斷連接怎麼處理 瀏覽:222
上世紀互聯網不發達程序員很難 瀏覽:841
語音識別android開源 瀏覽:762
地埋式垃圾壓縮中轉站 瀏覽:902
apachehttpdlinux 瀏覽:944
快遞員中通app預付款是什麼 瀏覽:843