Python垃圾回收(GC)三層心法,你瞭解到第幾層?

垃圾回收機制應該是面試最常問的問題了,那麼Python中的垃圾回收機制(Garbage Collection)是怎麼解決的呢?我記得每一本python入門的書籍都會說python中請不要擔憂內存泄漏這個 問題,那麼這個背後又是什麼原理,今天就來818。python

Python中的GC算法

分爲下三點:引用計數/標記-清除/分代回收面試

·引用計數(主要)算法

剛開始學習Python的時候老是會有人告訴你,萬物皆對象是一大特點。在Python中每個對象的核心就是一個結構體PyObject,它的內部有一個引用計數器(ob_refcnt)。bash

typedef struct_object {
 int ob_refcnt;
 struct_typeobject *ob_type;
} PyObject;
複製代碼
a=10
複製代碼

引用計數的意思就是,一個對象在它剛被New出來呱呱(gugu不是guagua)墜地的時候由於被New方法引用了因此他的引用計數就是1,若是它被引用(也就是在以前的基礎上 例如:b=a,被丟入函數列表等等被引用就會在引用計數上加1),若是引用它的對象被刪除的時候(在以前的基礎上DEL b)那麼它的引用計數就會減小一一直到當它的引用計數變爲0的時候,垃圾回收機制就會找上門作掉它(回收),腦補一下 :開門我是查水錶的。app

優勢/缺點:

由於引用計數是GC主要方法,來看一下優缺點。函數

優:學習

簡單,實時性(一旦爲零就不跟你多BB,作掉)spa

缺:指針

· 維護性高(簡單實時,可是額外佔用了一部分資源,雖然邏輯簡單,可是麻煩。比如你吃草莓,吃一次洗一下手,而不是吃完洗手。)code

· 不能解決的狀況:--->循環引用(以下):

a=[1,2]
b=[2,3]
a.append(b)
b.append(a)
DEL a
DEL b
複製代碼

說實話感受還有點像死鎖的問題,這種問題出如今能夠循環的結構中List Dict Object等等,如上代碼a、b間的引用都爲1,而a、b被引用的對象刪除後都各自減去1(因此他們各自的引用計數仍是1),這時候就尷尬了啊,都是1就有了免死金牌(一直是1不會變化了)。這樣的狀況單單靠引用計數就沒法解決了。 也爲咱們引入了下面的主題 標記-清除

·標記-清除: 標記清除就是用來解決循環引用的問題的只有容器對象纔會出現引用循環,好比列表、字典、類、元組。 首先,爲了追蹤容器對象,須要每一個容器對象維護兩個額外的指針, 用來將容器對象組成一個鏈表,指針分別指向先後兩個容器對象,方便插入和刪除操做。試想一下,如今有兩種狀況:

A:

a=[1,3]
b=[2,4]
a.append(b)
b.append(a)
del a
del b
複製代碼

B:

a=[1,3]
b=[2,4]
a.append(b)
b.append(a)
del a
複製代碼

Okay,如今開始說正題。在標記-清除算法中,有兩個集中營,一個是root鏈表(root object),另一個是unreachable鏈表

· 對於情景A,原來再未執行DEL語句的時候,a,b的引用計數都爲2(init+append=2),可是在DEL執行完之後,a,b引用次數互相減1。a,b陷入循環引用的圈子中,而後標記-清除算法開始出來作事,找到其中一端a,開始拆這個a,b的引用環(咱們從A出發,由於它有一個對B的引用,則將B的引用計數減1;而後順着引用達到B,由於B有一個對A的引用,一樣將A的引用減1,這樣,就完成了循環引用對象間環摘除。),去掉之後發現,a,b循環引用變爲了0,因此a,b就被處理到unreachable鏈表中直接被作掉

· 對於情景B,簡單一看那b取環後引用計數還爲1,可是a取環,就爲0了。這個時候a已經進入unreachable鏈表中,已經被判爲死刑了,可是這個時候,root鏈表中有b。若是a被作掉,那世界上還有什麼正義... ,在root鏈表中的b會被進行引用檢測引用了a,若是a被作掉了,那麼b就...涼涼,一審完事,二審a無罪,因此被拉到了root鏈表中。

QA: 爲何要搞這兩個鏈表

之因此要剖成兩個鏈表,是基於這樣的一種考慮:如今的unreachable可能存在被root鏈表中的對象,直接或間接引用的對象,這些對象是不能被回收的,一旦在標記的過程當中,發現這樣的對象,就將其從unreachable鏈表中移到root鏈表中;當完成標記後,unreachable鏈表中剩下的全部對象就是名副其實的垃圾對象了,接下來的垃圾回收只需限制在unreachable鏈表中便可。

分代回收:

瞭解分類回收,首先要了解一下,GC的閾值,所謂閾值就是一個臨界點的值。隨着你的程序運行,Python解釋器保持對新建立的對象,以及由於引用計數爲零而被釋放掉的對象的追蹤。從理論上說,建立==釋放數量應該是這樣子。可是若是存在循環引用的話,確定是建立>釋放數量,當建立數與釋放數量的差值達到規定的閾值的時候,噹噹噹當~分代回收機制就登場啦。

垃圾回收=垃圾檢測+釋放

分代回收思想將對象分爲三代(generation 0,1,2),0表明幼年對象,1表明青年對象,2表明老年對象。根據弱代假說(越年輕的對象越容易死掉,老的對象一般會存活更久。) 新生的對象被放入0代,若是該對象在第0代的一次gc垃圾回收中活了下來,那麼它就被放到第1代裏面(它就升級了)。若是第1代裏面的對象在第1代的一次gc垃圾回收中活了下來,它就被放到第2代裏面。gc.set_threshold(threshold0[,threshold1[,threshold2]])設置gc每一代垃圾回收所觸發的閾值。從上一次第0代gc後,若是分配對象的個數減去釋放對象的個數大於threshold0,那麼就會對第0代中的對象進行gc垃圾回收檢查。 從上一次第1代gc後,如過第0代被gc垃圾回收的次數大於threshold1,那麼就會對第1代中的對象進行gc垃圾回收檢查。一樣,從上一次第2代gc後,如過第1代被gc垃圾回收的次數大於threshold2,那麼就會對第2代中的對象進行gc垃圾回收檢查。

這算是簡單的講完了Python的GC,打完收工。

相關文章
相關標籤/搜索