天天看點

4.垃圾回收機制

垃圾回收機制(Garbage Collection:GC)基本是所有進階語言的标準配之一了

在一定程度上,能優化程式設計語言的資料處理效率和提高程式設計軟體開發軟體的安全性能

在 PYTHON 中的垃圾回收機制主要是以引用計數為主要手段

以标記清除和隔代回收機制作為輔助操作手段 完成對記憶體中無效資料的自動管理操作的!

4.1 引入計數

引用計數(Reference Counting:RC)是python中的垃圾回收機制的核心操作算法

該算法最早是 George E.Collins 在 1960 年首次提出的,并在大部分進階語言中沿用至 今,是很多進階語言的垃圾回收核心算法之一

  1. 什麼是引用計數

引用計數算法的核心思想是:當一個對象被建立或者拷貝時,引用計數就會+1,當這個對象 的多個引用變量,被銷毀一個時該對象的引用計數就會-1,如果一個對象的引用計數為 0 則 表示該對象已經不被引用,就可以讓垃圾回收機制進行清除并釋放該對象占有的記憶體空間 。

引用計數算法的優點是:操作簡單,實用性能優秀,能在最短的時間獲得并運算對象引用數

引用計數算法的缺點是:為了維護每個對象的引用技術操作算法,python必須提供和對象的記憶體消耗來維護引用計數,這樣就在無形中增加了記憶體負擔;同時引用計數對于循環應用、對象之間的互相引用,是無法進行引用計數操作的,是以就會造成常駐記憶體的情況

  1. Python中的引用計數

Python是一個面向對象的弱類型語言,所有的對象都是直接或者間接繼承object類型,

Object類型的核心其實就是一個結構體對象

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

在這個結構體中,ob_refcnt 就是對象的引用計數,當對象被建立或者拷貝時,該計數就會+1,當對象的引用變量被删除時,該計數就會-1,當引用計數為0時,對象就會被回收釋放了。在python中可以使用sys.getrefcount()來擷取一個對象的引用引用計數

4.垃圾回收機制

在上面定義一個類型,在p = Person()中,先執行個體化一個對象Person()(因為在解釋器中,代碼是從右往左解釋),在聲明一個變量p 引用這個對象的記憶體位址。此時的引用計數為1

在我們檢視引用計數的個數時,使用sys.getrefcount(p)時會通過p去檢視對象的引用的個數,此時會又會建立一個引用,是以此時的引用計數時2。但是在結果傳回之後,通過sys.getrefcount()的引用就會銷毀,是以就有了下面的案例:

4.垃圾回收機制

4.2. 标記-清除

PYTHON 中的标記-清除機制主要是針對可能産生循環引用的對象進行的檢測機制 在 PYTHON 中的基本不可變類型如 PyIntObject,PyStringObject 等對象的内部不會内聚其他對象的引用,是以不會産生循環引用,一般情況下循環引用總是發生在其他可變對象 的内部屬性中,如 list,dict,class 等等,使得該方法消耗的資源和程式中可變對象的數 量息息相關!

标記清除算法核心思想:首先找到 PYTHON 中的一批根節點對象,如 object 對象,通過根 節點對象可以找到他們指向的子節點對象,如果搜尋過程中有這個指向是從上往下的指向, 表示這個對象是可達的,否則該對象是不可達的,可達部分的對象在程式中需要保留下來, 不可達部分的對象在程式中是不需要保留的。

class A:
    pass


class B:
    pass


a = A()
b = B()
a.bb = b
b.aa = a
           
4.垃圾回收機制

如果代碼中執行了 del a del b 我們會發現,對象 A()和對象 B()依然有引用指向他們,如果是之前的引用計數的方式明 顯區分不了這樣的對象是否應該删除;但是标記-清除的方式,就可以标記出來對象 A() 和對象 B()是不可達對象,不需要保留,直接删除即可!

4.垃圾回收機制

4.3. 分代回收

PYTHON 中的分代回收機制,是一種通過空間換取時間效率的做法,PYTHON 内部處理機制 定義了三個不同的連結清單資料結構[第零代(年輕代),第 1 代(中年代),第 2 代(老年代)] PYTHON 為了提高程式執行效率,将垃圾回收機制進行了門檻值限定,0 代連結清單中的垃圾回收 機制執行最為密集,其次是 1 代,最後是 2 代; PYTHON 定義的這三個連結清單,主要是針對我們在程式中建立的對象,首先會添加到 0 代連結清單

4.垃圾回收機制

随後 0 代連結清單數量達到一定的門檻值之後,觸發 GC 算法機制,對 0 代對象進行符合規則的引 用計數運算,避免出現對象的延遲或者過早的釋放

4.垃圾回收機制

最終,觸發 GC 機制将已經沒有引用指向的對象進行回收,并将有引用繼續指向的對象 移動到第 1 代對象連結清單中;第 1 代對象連結清單的對象,就是比第 0 代對象連結清單中的對象可能存活更久的對象,GC 門檻值更大檢測頻率更慢,以提高程式執行效率

4.垃圾回收機制

以此類推直到一部分對象存活在第 2 代對象連結清單中,對象周期較長的可能跟程式的生命周 期一樣了。

備注:弱代假說:程式中年輕的對象往往死的更快,年老的對象往往存活更久

5.4. 垃圾回收處理

PYTHON 中的垃圾回收機制有了一定的了解之後,我們針對垃圾回收機制的操作通過代碼進行測試

PYTHON 中的 gc 子產品提供了垃圾回收處理的各項功能機制,必須 import gc 才能使用

gc.set_debug(flags):設定 gc 的 debug 日志,一般為 gc.DEBUG_LEAK

gc.collect([generation]):顯式進行垃圾回收處理,可以輸入參數~參數表示回收的對 象代數,0 表示隻檢查第 0 代對象,1 表示檢查第 0、1 代對象,2 表示檢查 0、1、2 代獨 對象,如果不傳遞參數,執行 FULL COLLECT,也就是預設傳遞 2

gc.set_threshold(threshold0 [, threshold2 [, threshold3]]):設定執行垃圾回 收機制的頻率

gc.get_count():擷取程式對象引用的計數器

gc.get_threshold():擷取程式自動執行 GC 的引用計數門檻值

以上是 PYTHON 中垃圾回收機制的基本操作,在程式開發過程中需要注意:

  •  項目代碼中盡量避免循環引用
  • 引入 gc 子產品,啟用 gc 子產品自動清理循環引用對象的機制
  • 将需要長期使用的對象集中管理,減少 GC 資源消耗
  •  gc 子產品處理不了重寫__del__方法導緻的循環引用,如果一定要添加該方法,需要顯式 調用 gc 子產品的 garbage 中對象的__del__方法進行處理