1. 程式人生 > 程式設計 >Python的垃圾回收機制詳解

Python的垃圾回收機制詳解

引用計數

在Python原始碼中,每一個物件都是一個結構體表示,都有一個計數字段。

typedef struct_object {
  int ob_refcnt;
  struct_typeobject *ob_type;
} PyObject;

PyObject是每個物件必有的內容,其中ob_refcnt就是作為引用計數。當一個物件有了新的引用時,它的ob_refcnt就會增加,引用它的物件被刪除時則減少。一旦物件的引用計數為0,該物件立即被回收,佔用空間就會被釋放。

優點

  • 簡單易用
  • 實時性好,一旦沒有引用就會被立即釋放

缺點

  • 需要額外空間去維護引用計數
  • 不能解決物件的迴圈引用

物件的迴圈引用
迴圈引用是指兩個物件相互引用且沒有外部變數引用其中任何一個,導致引用鍊形成一個環。

>>> a = {}    # 物件a的引用計數為1
>>> b = {}    # 物件b的引用計數為1
>>> a['b'] = b  # b的引用計數增加1
>>> b['a'] = a  # a的引用計數增加1
>>> del a     # a的引用計數減少1,最後a的引用為1
>>> del b     # b的引用計數減少1,最後b的引用為1

在執行完del操作之後,沒有任何引用指向a、b物件,但是由於這兩個物件各自包含一個對對方的引用,所以引用計數始終保持在1。

按照引用計數中記憶體回收的原理,由於a和b的計數不為0,所以在使用引用計數法進行記憶體管理的時候這兩個物件不會被回收,它們會一直駐留在記憶體中,造成記憶體洩露。

標記清除

標記清除機制主要用於解決迴圈引用問題。

標記清除演算法是一種基於追蹤回收(tracing GC)技術實現的垃圾回收演算法。主要分為兩個階段:

  • 標記階段,GC會將所有的活動物件打上標記
  • 對那些沒有打上標記的非活動物件進行回收

區分活動物件與非活動物件

物件之間通過引用即指標連線在一起,構成一個有向圖,物件就是這個有向圖的節點,而引用關係構成這個有向圖的邊。從根物件(root object)出發,沿著有向邊遍歷物件,可達的物件會被標記為活動物件,不可達的物件就是要被清除的非活動物件。

根物件一般是全域性變數、呼叫棧、暫存器等。

適用範圍

標記清除演算法作為Python輔助的垃圾收集技術,主要處理的是容器物件,因為對於字串、數值物件等,不可能造成迴圈引用的問題,Python會使用一個雙向連結串列將這些容器物件組織起來。

對於標記清除演算法來說,有一個比較明顯的缺點:為了清除非活動物件,需要掃描整個堆記憶體,哪怕只剩下小部分活動物件也需要掃描所有物件。

分代回收

分代回收是一種以空間換時間的操作方式,建立在標記清除技術的基礎之上,也是Python輔助的垃圾收集技術,主要用於處理容器物件。

Python會將記憶體根據物件的存活時間劃分為不同的集合,每個集合稱為一個代,主要會被分為3代:年輕代。中年代和老年代,它們會對應3個連結串列,對應的垃圾收集頻率隨著物件存活時間的增大而減小。

新建立的物件都會被分配在年輕代,當年輕代連結串列總數達到上限時,會觸發Python的垃圾回收機制,對可回收物件進行回收,而那些不可回收的物件會被移到中年代去。依此類推,老年代物件是存活時間最久的物件,甚至有可能存活在整個系統的生命週期內。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。