垃圾回收機制
我們定義變數會申請記憶體空間來存放變數的值,而記憶體的容量是有限的,當一個變數值沒有用了(稱為垃圾),就應該將其佔用的記憶體給回收掉。變數名是訪問到變數的唯一方式,所以當一個變數值沒有任何關聯的變數名時,我們就無法訪問到該變量了,該變數就是一個垃圾,會被python解釋的垃圾回收機制自動回收。
一、什麼是垃圾回收機制
垃圾回收機制(簡稱GC)是python直譯器自帶的一種機制,專門用來回收不可用的變數值所佔用的記憶體空間
二、為什麼要有垃圾回收機制
程式執行過程中會申請大量的記憶體空間,而對於一些無用的記憶體空間,如果不及時清理的話,會導致記憶體使用完(記憶體溢位),導致程式崩潰,因此,記憶體管理是一件重要且繁雜的事情,而python直譯器自帶的垃圾回收機制把程式設計師從繁雜的記憶體管理中解放出來。
三、垃圾回收機制原理分析
python的GC模組主要採用了‘引用計數’來跟蹤和回收垃圾。在引用計數的基礎上,還可以通過‘標記-清除’來解決容器物件可能產生的迴圈引用的問題,並且通過‘分代回收’來以空間換取時間的方式進一步提高垃圾回收的效率。
1,引用計數
引用計數就是:變數值被變數名關聯的次數
如:
引用計數增加
x=10(此時,變數值10的引用次數為1)
y=x(此時,把x的記憶體地址給了y,此時,變數值10 的引用計數為2)
引用計數減少
x=3(此時,x和10解除關係,與3建立關係,變數值10的引用計數為1)
del y(del是解除變數名y與變數值10之間的關係,變數值10的引用計數為0),變數值10的引用計數為0,其佔用的記憶體空間就會被回收
2,迴圈引用
引用計數機制執行效率問題:變數值被關聯次數的增加或減少,都會引發引用計數機制的執行,這明視訊記憶體在效率問題,這就是引用計數的一個軟肋,但引用計數還存在一個致命弱點,即迴圈引用(也稱交叉引用)。
# 變數名l1指向列表1,變數名l2指向列表2,如下 >>> l1=['列表1中的第一個元素']# 列表1被引用一次 >>> l2=['列表2中的第一個元素']# 列表2被引用一次 >>> l1.append(l2)# 把列表2追加到l1中作為第二個元素,列表2的引用計數為2 >>> l2.append(l1)# 把列表1追加到l2中作為第二個元素,列表1的引用計數為2 # l1與l2 del l1#列表1的引用計數為1 del l2#列表2的引用計數為1
現在列表1和列表2都沒被其他變數名關聯,但引用計數不為0,所以不會被回收,這就是迴圈引用的危害,為解決這問題,python引進了‘標記-清除’,‘分代回收’。
3,標記-清除
容器物件(list,set,dict,class,instance)都可以包含其他物件的引用,所以都可能產生迴圈引用。在瞭解‘標記-清除’之前,先得知道一個知識點:記憶體中有兩塊區域:堆區與棧區,在定義變數時,變數名放在棧區,變數值放在堆區,記憶體管理是對堆區的管理。
當有效記憶體空間被耗盡的時候,就會停止整個程式,然後進行兩項工作,第一是標記,第二是清除
標記:遍歷所有的GC Roots物件(棧區中的所有內容或者執行緒都可以作為GC Roots物件),然後將所有GC Roots物件可以直接訪問或間接訪問的物件標記為存活物件。
清除:遍歷堆區中所有的物件,將沒有標記的物件全部清除
4,分代回收
基於引用計數的回收機制,每次回收記憶體,需要把所有的物件的引用計數都遍歷一遍,這是非常耗費時間的,於是引入分代回收提高回收效率,採用‘空間換取時間的策略’。
分代:在多次掃描的情況下,都沒有被回收的變數值,GC機制會認為,該變數值的級別會增高,對其掃描的頻率會降低。
分代指的是根據存活時間來劃分變數值的等級(也就是不同的代)
新定義的變數值,會放在新生代中,假設每隔1分鐘掃描一次,如果發現變數值依然存活,那該變數值的等級會提高,當權重大於3(假設為3),會放到青春代中,每隔5分鐘掃描一次,繼續存活下去,權重繼續增高,當權重大於10(假設為10),會被放到老年代中,次時每隔10分鐘掃描一次,以此類推。等級越高,被垃圾回收掃描的頻率越低。
回收:依然是引用計數作為回收依據