python垃圾回收库_Python垃圾回收機制

Python GC主要運用了“引用計數”(reference counting)來跟蹤和回收垃圾。

在引用計數的基礎上,通過“標記-清除”(mark and sweep)解決容器對象可能產生的循環引用的問題。通過“分代回收”(generation collection)以空間換取時間來提高垃圾回收效率。

引用計數

在Python中,大多數對象的生命周期都是通過對象的引用計數來管理的。從廣義上來講,引用計數也是一種垃圾收集機制。並且也是一種最直觀。最簡單的垃圾收集技術。

原理:當一個對象的引用被創建或者復制時。對象的引用計數加1。當一個對象的引用被銷毀時,對象的引用計數減1;當對象的引用計數降低為0時,就意味着對象已經沒有被不論什么人使用了。能夠將其所占用的內存釋放了。

盡管引用計數必須在每次分配和釋放內存的時候增加管理引用計數的動作,然而與其它主流的垃圾收集技術相比。引用計數有一個最大的有點,即“實時性”。不論什么內存,一旦沒有指向它的引用,就會馬上被回收。而其它的垃圾收集計數必須在某種特殊條件下(比方內存分配失敗)才干進行無效內存的回收。

引用計數機制執行效率問題:引用計數機制所帶來的維護引用計數的額外操作與Python執行中所進行的內存分配和釋放。引用賦值的次數是成正比的。而這點相比其它主流的垃圾回收機制,比方“標記-清除”,“停止-復制”,是一個弱點,由於這些技術所帶來的額外操作基本上僅僅是與待回收的內存數量有關。

假設說運行效率還不過引用計數機制的一個軟肋的話,那么非常不幸。引用計數機制還存在着一個致命的弱點,正是因為這個弱點,使得俠義的垃圾收集從來沒有將引用計數包括在內,能引發出這個致命的弱點就是循環引用(也稱交叉引用)。

問題:

循環引用能夠使一組對象的引用計數不為0。然而這些對象實際上並沒有被不論什么外部對象所引用。它們之間僅僅是相互引用。這意味着不會再有人使用這組對象。應該回收這組對象所占用的內存空間,然后因為相互引用的存在,每個對象的引用計數都不為0。因此這些對象所占用的內存永遠不會被釋放。比方:

a = []

b = []

a.append(b)

b.append(a)

print a

[[[…]]]

print b

[[[…]]]

這一點是致命的。這與手動進行內存管理所產生的內存泄露毫無差別。

要解決問題。Python引入了其它的垃圾收集機制來彌補引用計數的缺陷:“標記-清除”。“分代回收”兩種收集技術。

標記-清除

“標記-清除”是為了解決循環引用的問題。能夠包括其它對象引用的容器對象(比方:list,set,dict。class。instance)都可能產生循環引用。

我們必須承認一個事實。如果兩個對象的引用計數都為1。可是只存在他們之間的循環引用,那么這兩個對象都是須要被回收的。也就是說。它們的引用計數盡管表現為非0。但實際上有效的引用計數為0。我們必須先將循環引用摘掉,那么這兩個對象的有效計數就現身了。如果兩個對象為A、B。我們從A出發,由於它有一個對B的引用,則將B的引用計數減1;然后順着引用達到B,由於B有一個對A的引用,相同將A的引用減1,這樣,就完畢了循環引用對象間環摘除。

可是這樣就有一個問題,假設對象A有一個對象引用C,而C沒有引用A。假設將C計數引用減1,而最后A並沒有被回收,顯然。我們錯誤的將C的引用計數減1,這將導致在未來的某個時刻出現一個對C的懸空引用。這就要求我們必須在A沒有被刪除的情況下復原C的引用計數,假設採用這種方案。那么維護引用計數的復雜度將成倍添加。

原理:“標記-清除”採用了更好的做法,我們並不修改真實的引用計數,而是將集合中對象的引用計數復制一份副本。修改該對象引用的副本。

對於副本做不論什么的修改,都不會影響到對象生命走起的維護。

這個計數副本的唯一作用是尋找root object集合(該集合中的對象是不能被回收的)。

當成功尋找到root object集合之后。首先將如今的內存鏈表一分為二,一條鏈表中維護root object集合。成為root鏈表,而另外一條鏈表中維護剩下的對象,成為unreachable鏈表。之所以要剖成兩個鏈表,是基於這種一種考慮:如今的unreachable可能存在被root鏈表中的對象。直接或間接引用的對象,這些對象是不能被回收的,一旦在標記的過程中。發現這種對象,就將其從unreachable鏈表中移到root鏈表中。當完畢標記后。unreachable鏈表中剩下的全部對象就是名副事實上的垃圾對象了,接下來的垃圾回收僅僅需限制在unreachable鏈表中就可以。

分代回收

背景:分代的垃圾收集技術是在上個世紀80年代初發展起來的一種垃圾收集機制,一系列的研究表明:不管使用何種語言開發。不管開發的是何種類型,何種規模的程序,都存在這樣一點同樣之處。

即:一定比例的內存塊的生存周期都比較短,一般是幾百萬條機器指令的時間。而剩下的內存塊。起生存周期比較長。甚至會從程序開始一直持續到程序結束。

從前面“標記-清除”這種垃圾收集機制來看。這種垃圾收集機制所帶來的額外操作實際上與系統中總的內存塊的數量是相關的。當須要回收的內存塊越多時,垃圾檢測帶來的額外操作就越多,而垃圾回收帶來的額外操作就越少;反之。當需回收的內存塊越少時,垃圾檢測就將比垃圾回收帶來更少的額外操作。為了提高垃圾收集的效率。採用“空間換時間的策略”。

原理:將系統中的全部內存塊依據其存活時間划分為不同的集合,每個集合就成為一個“代”,垃圾收集的頻率隨着“代”的存活時間的增大而減小。

也就是說,活得越長的對象。就越不可能是垃圾,就應該降低對它的垃圾收集頻率。那么怎樣來衡量這個存活時間:一般是利用幾次垃圾收集動作來衡量,假設一個對象經過的垃圾收集次數越多,能夠得出:該對象存活時間就越長。

舉例:

當某些內存塊M經過了3次垃圾收集的清洗之后還存活時,我們就將內存塊M划到一個集合A中去。而新分配的內存都划分到集合B中去。當垃圾收集開始工作時,大多數情況都僅僅對集合B進行垃圾回收,而對集合A進行垃圾回收要隔相當長一段時間后才進行,這就使得垃圾收集機制須要處理的內存少了,效率自然就提高了。

在這個過程中,集合B中的某些內存塊由於存活時間長而會被轉移到集合A中,當然,集合A中實際上也存在一些垃圾,這些垃圾的回收會由於這樣的分代的機制而被延遲。

在Python中。總共同擁有3“代”,也就是Python實際上維護了3條鏈表。

具體能夠查看Python源代碼具體了解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值