Python 垃圾回收機制和如何解決循環引用

  • 引用計數:是一種垃圾收集機制,而且也是一種最直觀,最簡單的垃圾收集技術, 當一個對象的引用被創建或者複製時,對象的引用計數加 1;當一個對象的引用被銷燬時,對象的引用計數減 1;當對象的引用計數減少爲 0 時,就意味着對象已經沒有被任何人使用了,可以將其所佔用的內存釋放了。雖然引用計數必須在每次分配和釋放內存的時候加入管理引用計數的動作,然而與其他主流的垃圾收集技術相比,引用計數有一個最大的有點,即“實時性”,任何內存,一旦沒有指向它的引用,就會立即被回收。而其他的垃圾收集計數必須在某種特殊條件下(比如內存分配失敗)才能進行無效內存的回收。
  • 引用計數機制執行效率問題:引用計數機制所帶來的維護引用計數的額外操作與 Python 運行中所 進行的內存分配和釋放,用賦值的次數是成正比的。而這點相比其他主流的垃圾回收機制,比如“標 記-清除”,“停止-複製”,是一個弱點,因爲這些技術所來的額外操作基本上只是與待回收的內存 數量有關。
  • 如果說執行效率還僅僅是引用計數機制的一個軟肋的話,那麼很不幸,引用計數機制還存在着一個 致命的弱點,正是由於這個弱點,使得俠義的垃圾收集從來沒有將引用計數包含在內,能引發出這個致 命的弱點就是循環引用(也稱交叉引用)。
  • 問題說明: 循環引用可以使一組對象的引用計數不爲 0,然而這些對象實際上並沒有被任何外部對象所引用, 它們之間只相互引用。這意味着不會再有人使用這組對象,應該回收這組對象所佔用的內存空間,然 後由於相互引用的存在,每一個對象的用計數都不爲 0,因此這些對象所佔用的內存永遠不會被釋放。 比如:這一點是致命的,這與手動進行內存管理所產生的內存泄露毫無區別。
  • 要解決這個問題,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 年代初發展起來的一種垃圾收集機制,一系 列的研究表明:無論使用何種語言開發,無論開發的是何種類型,何種規模的程序,都存在這樣一點相 同之處。即:一定比例的內存塊的生存週期都比較短,通常是幾百萬條機器指令的時間,而剩下的內存 塊,起生存週期比較長,甚至會從程序開始一直持續到程序結束。
  • 從前面“標記-清除”這樣的垃圾收集機制來看,這種垃圾收集機制所帶來的額外操作實際上與系統 中總的內存塊的數量是相關的,當需要回收的內存塊越多時,垃圾檢測帶來的額外操作就越多,而垃圾 回收帶來的額外操作就越少;反之,當需回收的內存塊越少時,垃圾檢測就將比垃圾回收帶來更少的額 外操作。爲了提高垃圾收集的效率,採用“空間換時間的策略”。
  • 原理:將系統中的所有內存塊根據其存活時間劃分爲不同的集合,每一個集合就成爲一個“代”, 垃圾收集的頻率隨着“代”的存活時間的增大而減小。也就是說,活得越長的對象,就越不可能是垃圾, 就應該減少對它的垃圾收集頻率。那麼如何來衡量這個存活時間:通常是利用幾次垃圾收集動作來衡量, 如果一個對象經過的垃圾收集次數越多,可以得出:該對象存活時間就越長。
  • 發表評論
    所有評論
    還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
    相關文章