Python垃圾回收機制

 

Python垃圾回收機制

 

知識點的鋪墊

  對象和引用python

    python做爲一門動態語言,一個簡單的賦值語句也是很值得研究的,重要特色就是引用對象分離。算法

a = 1

    其中整數1是一個對象,而a是一個引用。利用賦值語句,引用a指向對象1。緩存

    爲了探索對象在內存的存儲,咱們能夠利用Python內置函數id(),來查看對象的內存地址。 app

a = 1
b = 1

print(id(a))
print(id(b))

#  4305308800
#  4305308800

  能夠看出 a 和 b 其實是指向同一個對象的兩個引用。ide

  爲了檢驗兩個引用指向同一個對象,咱們能夠用is關鍵字來判斷引用所指的對象是否相同函數

# True
a = 1
b = 1
print(a is b)

# False
a = "good"
b = "good"
print(a is b)

# False
a = []
b = []
print(a is b)

  從上面代碼能夠看到,因爲Python緩存了整數和短字符串,所以每一個對象只有一份,增長的不過是引用,而不是對象。可是,list,dict對象能夠有多個相同的對象,每次賦值都是建立新的對象。post

  咱們每次建立對象都會分配內存,容器對象跟數字不同呢?這是由於Python的內存池機制。性能

  Python內存池指針

      若是頻繁的調用 malloc 與 free 時,是會產生性能問題的.再加上頻繁的分配與釋放小塊的內存會產生內存碎片.  對象

Python 在這裏主要乾的工做有:

  若是請求分配的內存在1~256字節之間就使用本身的內存管理系統,不然直接使用 malloc.

  這裏仍是會調用 malloc 分配內存,但每次會分配一塊大小爲256k的大塊內存.

引用計數

在Python中,每一個對象都存有指向該對象的引用總數,即引用計數(reference count)。

Python的垃圾回收機制就是以引用計數爲主,當一個對象的引用計數爲0時,表明它是垃圾將要被回收。

這是引用計數增長的狀況,

from sys import getrefcount
a = [1, 2, 3]
# 對象被建立
# 對象被當成參數傳給函數
print(getrefcount(a))

b = a
# 另外的引用被建立
print(getrefcount(a))

c = [a, a]
# 做爲容器對象的一個元素
print(getrefcount(a))

  因爲a和b當成了參數傳給getrefcount(),因此結果返回2,3,5。      

這是引用計數減小的狀況,

from sys import getrefcount
a = [1, 2, 3]
b = a
c = [1, a]
print(getrefcount(a))
# 一個本地引用離開它做用域 eg:getrefcount()結束時

# del b
# 對象的別名被顯示銷燬
# print(getrefcount(a))

# b = 1
# 對象的別名被從新賦值其餘對象
# print(getrefcount(a))

# b.remove(a)
# 對象從容器中刪除
# print(getrefcount(a))

# del c
# 對象所在容器被刪除
# print(getrefcount(a))  

如今咱們知道了對象引用計數的狀況,那麼咱們再來看一個狀況。

a = [1, 2, 3]
b = [4, 5, 6]
a.append(b)
b.append(a)

  a和b互相引用,致使a和b的引用計數不可能爲0,致使內存暴露,因此python註定會再引入新的垃圾回收機制。

  爲了解決這種孤立的引用環,Python引入了一個有效引用計數的概念,引入了標記清除法。

標記清除

『標記清除(Mark—Sweep)』算法是一種基於追蹤回收(tracing GC)技術實現的垃圾回收算法。它分爲兩個階段:第一階段是標記階段,GC會把全部的『活動對象』打上標記,第二階段是把那些沒有標記的對象『非活動對象』進行回收。那麼GC又是如何判斷哪些是活動對象哪些是非活動對象的呢?

對象之間經過引用(指針)連在一塊兒,構成一個有向圖,對象構成這個有向圖的節點,而引用關係構成這個有向圖的邊。從根對象(root object)出發,沿着有向邊遍歷對象,可達的(reachable)對象標記爲活動對象,不可達的對象就是要被清除的非活動對象。根對象就是全局變量、調用棧、寄存器。

mark-sweepg

在上圖中,咱們把小黑圈視爲全局變量,也就是把它做爲root object,從小黑圈出發,對象1可直達,那麼它將被標記,對象二、3可間接到達也會被標記,而4和5不可達,那麼一、二、3就是活動對象,4和5是非活動對象會被GC回收。

標記清除算法做爲Python的輔助垃圾收集技術主要處理的是一些容器對象,好比list、dict、tuple,instance等,由於對於字符串、數值對象是不可能形成循環引用問題。Python使用一個雙向鏈表將這些容器對象組織起來。不過,這種簡單粗暴的標記清除算法也有明顯的缺點:清除非活動的對象前它必須順序掃描整個堆內存,哪怕只剩下小部分活動對象也要掃描全部對象。

分代回收

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

 
 
 
相關文章
相關標籤/搜索