1. 程式人生 > 其它 >Python之垃圾回收機制

Python之垃圾回收機制

什麼是垃圾回收

    當為一個變數分配資料的時候,python會在記憶體中分配一部分空間,使用者儲存此資料,但記憶體空間總是有限的,如果一直佔用記憶體空間,記憶體遲早會溢位,所以,程式中需要把無用的資料從記憶體中刪除,回收記憶體空間,這個過程就叫作垃圾回收。
    python採用的是引用計數機制為主,標記-->清除和分代收集(隔代回收)兩種機制為輔的策略。

垃圾回收中的計數

    python裡每一個東西都是物件,它們的核心就是一個結構體:PyObject
    PyObject是每個物件必有的內容,其中ob_refcnt就是做為引用計數。當一個物件有新的引用時,它的ob_refcnt就會增加,當引用它的物件被刪除,它的ob_refcnt就會減少
    簡單來說,當一個變數被賦值一次時,這個變數此時的計數為1,當這個變數被其它變數引用時,此時的計數將會加1,當引用它的物件被刪除時,它的計數將會減少1,當計數為0時,此時的這個變數對應的資料則為垃圾資料,此資料就會被回收。

import sys
name = 'a'
print(sys.getrefcount(name))  # 返回值11,初始值為什麼是11而不是1?後續需要查答案
name1 = name
print(sys.getrefcount(name))  # 返回值12,多引用了一次,計數器加1
name2 = name1
print(sys.getrefcount(name))  # 返回值13,再次引用,計數器加1
del name1
print(sys.getrefcount(name))  # 返回值12,刪除一個變數,計數器減1

垃圾回收中的 標記->回收

計數問題產生了一個新的問題,就是迴圈引用無法回收,什麼是迴圈引用,程式碼如下:

l1 = [1, 2]  # 此時l1的計數假設為1
l2 = [3, 4]  # 此時l2的計數假設為1
l1.append = l2  # 此時l2的計數為2
l2.append = l1  # 此時l1的計數為2
del l1  # 此時l1的計數為1
del l2  # 此時l2的計數為1

上面的程式碼可以明白,由於互相引用的情況下產生了迴圈引用,此時刪除了l1與l2兩個變數是無法讓計數變為0的,那麼記憶體就無法得到釋放,為了解決這個問題,python出現了一個 標記 -> 回收機制。
當記憶體不夠用時,python會去檢查程式碼中的所有記憶體中的資料值,如果發現了一個迴圈引用的值時,就會打上標記,當python檢查完記憶體中所有資料值時,將會一次性回收所有的被打過標記的資料值。

Python中的分代回收

分代回收是一種以空間換時間的機制。

  1. python會將記憶體物件根據存活時間劃分為三個不同的集合。每個集合可以稱為一個代。可以理解為“第0代”,“第1代”,“第2代”,他們對應的是3個連結串列,它們的垃圾收集頻率隨著物件存活時間的增大而減小。
  2. 新建立的物件都被分配到“第0代”中,當“第0代”的連結串列總數量達到上限時,python的垃圾回收機制將被觸發,將回收可回收物件,並將不可回收物件放到“第1代”連結串列中,依次類推,“第2代”連結串列中的物件為存活時間最久的物件,甚至存活整個系統的生命週期內。
  3. 分代回收是建立在標記清除技術基礎之上。分代回收同樣作為Python的輔助垃圾收集技術處理那些容器物件。

而分代回收被建立的原因就是為了節省系統執行的資源,系統可能會頻繁的去檢查“第0代”連結串列中的物件,而對於“第1代”連結串列中的物件則相對來說不會太過頻繁去檢查,而“第2代”連結串列中的物件可能很長時間才會檢查一次,以此來節省運算資源。