1. 程式人生 > >python之MRO和垃圾回收機制

python之MRO和垃圾回收機制

一、MOR

1、C3演算法簡介

為了解決原來基於深度優先搜尋演算法不滿足本地優先順序,和單調性的問題。
python2.3版本之後不管是新式類還是經典類,查詢繼承順序都採用C3演算法

 

2、演算法原理

C3演算法的本質就是Merge, 不斷地把mro()函式返回的佇列進行Merge,規則如下:
(0)
    首先把要查詢的類的所有父類的mro找出來,再把所有父類的mro和所有父類進行歸併演算法

(1) 
    如果第一個序列的第一個元素,是後續序列的第一個元素,或者在後續序列中沒有再次出現,則將這個元素合併到最終的方法解析順序序列中,
    並從當前操作的全部序列中刪除。

(
2) 如果不符合,則跳過此元素,查詢下一個列表的第一個元素,重複1的判斷規則。

 

3、示例

class A(object): pass
class B(A): pass
class C(A): pass
class D(B): pass
class E(C): pass
class F(D,E): pass

# F的mro順序:
# 第一步 找出F所有的父類的MRO
# D  [DBAO]
# E  [ECAO]
# 第二步 把所有父類的MRO 以及 所有的父類做歸併演算法
# [DBAO] [ECAO] [DE]
# F + merge([DBAO] [ECAO] [DE])
# 取第一個序列的第一個元素D,它是後續序列[DE]的第一個元素,那麼D是最終序列的第一個元素 # 把D從全部序列中刪除 # B在後續序列中沒有再次出現,那麼B是最終序列的第二個元素 # FD + merge([BAO] [ECAO] [E]) # A在後續序列中出現了,跳過A,查詢下一個列表的第一個元素E,E是後續序列的第一個元素,取E # FDB + merge([AO] [ECAO] [E]) # FDBE + merge([AO] [CAO]) # FDBEC + merge([AO] [AO]) # FDBECAO print(F.__mro__)

 

二、垃圾回收機制

1、引用計數機制

python中的垃圾回收機制是:引用計數主、標記清除和分代回收為輔
python裡每一個東西都是物件,它們的核心就是一個結構體:PyObject
Python物件和引用是分離的

PyObject是每個物件必有的內容,其中ob_refcnt就是做為引用計數。當一個物件有新的引用時,它的ob_refcnt就會增加,當引用它的物件被刪除,
它的ob_refcnt就會減少。

當引用計數為0時,該物件生命就結束了,就會被清除了。


檢視某個變數的引用計數
from sys import getrefcount
print(getrefcount(變數))


引用計數機制的優點:
1. 簡單
2. 實時性
    一旦沒有引用,記憶體就直接釋放了。不用像其他機制等到特定時機。實時性還帶來一個好處:處理回收記憶體的時間分攤到了平時。

引用計數機制的缺點:
1. 維護引用計數消耗資源
2. 迴圈引用的時候,這個物件的引用計數永遠不會為0,這個物件就會一直存在
    d = [1,2,3]
    f = [4,5,6]
    d.append(f)
    f.append(d)

 

2、標記清除

標記清除(Mark—Sweep)演算法是一種基於追蹤回收(tracing GC)技術實現的垃圾回收演算法。
它分為兩個階段:
    第一階段是標記階段,GC會把所有的活動物件打上標記。
    第二階段是把那些沒有標記的非活動物件進行回收。

如何判斷哪些是活動物件哪些是非活動物件:
物件之間通過引用(指標)連在一起,構成一個有向圖,物件構成這個有向圖的節點,而引用關係構成這個有向圖的邊。從根物件(root object)出發,
沿著有向邊遍歷物件,可達的(reachable)物件標記為活動物件,不可達的物件就是要被清除的非活動物件。根物件就是全域性變數、呼叫棧、暫存器。

示例圖:


在上圖中,我們把小圈當做全域性變數,也就是把它作為root object,
從小圈出發,物件1可直達,那麼它將被標記,物件2、3、6可間接到達也會被標記,
而4和5不可達,那麼1、2、3、6就是活動物件,4和5是非活動物件會被GC回收。

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

不過,這種簡單粗暴的標記清除演算法也有明顯的缺點:清除非活動的物件前它必須順序掃描整個堆記憶體,哪怕只剩下小部分活動物件也要掃描所有物件。

 

3、分代回收

1. 理論解釋
分代回收是一種以空間換時間的操作方式,Python將記憶體根據物件的存活時間劃分為不同的集合,每個集合稱為一個代,Python將記憶體分為了3“代”,分別
為年輕代(第0代)、中年代(第1代)、老年代(第2代),他們對應的是3個連結串列,它們的垃圾收集頻率與物件的存活時間的增大而減小。新建立的物件都會
分配在年輕代,年輕代連結串列的總數達到上限時,Python垃圾收集機制就會被觸發,把那些可以被回收的物件回收掉,而那些不會回收的物件就會被移到中年
代去,依此類推,老年代中的物件是存活時間最久的物件,甚至是存活於整個系統的生命週期內。同時,分代回收是建立在標記清除技術基礎之上。分代回收
同樣作為Python的輔助垃圾收集技術處理那些容器物件。



2. 簡單來說
python記憶體中存放了三代資料,每一代都是連結串列
    0代 年輕的一代
    域值(700, 10, 10)
    當0代連結串列達到域值上限後,觸發0帶的回收
    0代被觸發回收之後沒有被回收的物件放入1代
    0代被回收10次的時候觸發1代回收
    1代沒有被回收物件放入2代
    1代觸發10次的時候觸發2代回收

 

4、總結

當某個條件達到後,觸發了垃圾回收機制,首先在0代中先做引用計數,把引用為0的物件清除,然後再做標記清除,把沒用的迴圈引用物件也清除,
再把沒有清除的物件放到1代,當觸發了10次0代回收機制後,會觸發1代的垃圾回收,然後先做引用計數,再做標記清除,觸發10次1代垃圾回收後,
會觸發2代垃圾回收...