Python 中的垃圾回收機制--備忘

GC做爲現代編程語言的自動內存管理機制,專一於兩件事:1. 找到內存中無用的垃圾資源 2. 清除這些垃圾並把內存讓出來給其餘對象使用。GC完全把程序員從資源管理的重擔中解放出來,讓他們有更多的時間放在業務邏輯上。但這並不意味着碼農就能夠不去了解GC,畢竟多瞭解GC知識仍是有利於咱們寫出更健壯的代碼。git

引用計數

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

什麼是循環引用?A和B相互引用而再沒有外部引用A與B中的任何一個,它們的引用計數雖然都爲1,但顯然應該被回收,例子:github

recycle-refrence.jpg

在這個例子中程序執行完del語句後,A、B對象已經沒有任何引用指向這兩個對象,可是這兩個對象各包含一個對方對象的引用,雖然最後兩個對象都沒法經過其它變量來引用這兩個對象了,這對GC來講就是兩個非活動對象或者說是垃圾對象,可是他們的引用計數並無減小到零。所以若是是使用引用計數法來管理這兩對象的話,他們並不會被回收,它會一直駐留在內存中,就會形成了內存泄漏(內存空間在使用完畢後未釋放)。爲了解決對象的循環引用問題,Python引入了標記-清除和分代回收兩種GC機制。算法

標記清除

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

對象之間經過引用(指針)連在一塊兒,構成一個有向圖,對象構成這個有向圖的節點,而引用關係構成這個有向圖的邊。從根對象(root object)出發,沿着有向邊遍歷對象,可達的(reachable)對象標記爲活動對象,不可達的對象就是要被清除的非活動對象。根對象就是全局變量、調用棧、寄存器。編程語言

mark-sweepg

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

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

分代回收

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

相關文章
相關標籤/搜索