本文目錄一覽:
python垃圾回收機制(超詳細)
概述:引用計數為主,標記清除,分代回收為輔
1引用計數
python程序中創建的所有的對象都是放在一個雙向環狀循環鏈表refchain上的
如下對象被創建時,在C語言底層實際結構
name=’string’
c語言內底部創建成 [上一個對象,下一個對象,類型,引用個數]
age=18
c語言內底部創建成[上一個對象,下一個對象,類型,引用個數,val=18]
hobby=[‘籃球’, ‘擼鐵’,‘玩’]
c語言內底部創建成[上一個對象,下一個對象,類型,引用個數,item=元素, 元素個數]
當python程序運行時,會根據數據類型的不同找到其對應的結構體,根據結構體中的字段來進行創建相關的數據,然後將對象添加到refchain雙向鏈表中
每個對象中有ob_refcnt就是應用計數器,默認為1,當有其他變量引用對象時,引用計數器就會+1
當引用計數器為0時,意味着沒人使用這個對象了,這個對象就是垃圾,就會回收
回收步驟 :1對象從refchain鏈表移除 2將對象銷毀,內存回收
2 標記清除
為什麼要標記清除 :為了解決引用計數器循環引用的不足,循環引用可能導致內存泄漏
實現:在python的底層,再維護一個鏈表,鏈表中專門放那些可能存在循環引用的對象(list/tuple/dict/set)
在python內部,某種情況下觸發,回去掃描可能存在循環引用鏈表中的每個元素,檢查是否是循環引用,如果有,則讓雙方的引用計數器-1,如果是0,則垃圾回收
3 分代回收
為什麼要分代回收: 不知道什麼情況下觸發掃描,可能存在循環引用的鏈表掃描代價大,每次掃描很久
將可能存在循環引用的對象維護成3個鏈表
0代:0代中對象個數達到700個掃描一次
1代:0代掃描10次,則1代掃描1次
2代:1代掃描10次,則2代掃描1次
過程:當我們創建了一個對象a=1,這個對象只會加到refchain鏈表中,而當我們創建了一個可能存在循環引用的對象b=[]一個列表時,這個對象不但會加到refchain鏈表中,還會加到分帶回收的0代鏈表中,當0代鏈表中對象達到700個,GC開始掃描,如果是循環引用,那就自減1,減完以後,如果是垃圾,那就自動回收,如果不是垃圾,那就將這些對象升級到1代鏈表中,就這樣掃描一遍,此時0代鏈表也會記錄自己掃描了1次,等到下次0代鏈表的對象又達到了700個,繼續上述步驟,就這樣執行了10次,才會觸發執行掃描1代鏈表,1代鏈表和2代鏈表中的操作和0代中一樣。
4 小結(面試可以這麼說)
在python中,維護了一個refchain的雙向循環環狀鏈表,這個鏈表中存儲程序創建的所有對象,每種類型的對象中都有一個0b_refcnt引用計數器的值,默認為1,當引用計數器變為0時會進行垃圾回收(對象銷毀,refchain中移除)
但是,在python 中,對於那些可以有多個元素組成的對象可能會存在循環引用的問題,為了解決這個問題,python又引入了標記清除和分代回收,在其內部維護了四個鏈表
refchain
0代 700個對象觸發
1代 0代十次執行一次1代
2代 1代十次執行一次2代
當 每個鏈表達到閾值時,就會觸發掃描鏈表進行標記清除操作,有循環則各自-1,為0時,直接回收,銷毀,清除
But, 在上面的垃圾回收機制的步驟中,python提供了優化機制
緩存
小整數對象池
為了避免重複創建和銷毀一些常用對象,維護了一個小整數對象池
-5~257的地址內存是一定的,這些對象是pyhton事先幫我們創建好了
free_list(會有大小限制)
當一個對象的引用計數為0時,按理說應該回收,但是python沒有回收,而是把這個對象放到了一個free_list中當緩存,以後再去使用時,不在重新開闢內存,而是直接使用free_list
比如現在一個對象V=3.14 ,我現在把他del V, 代表引用計數為0 了,但是這塊地址我不會回收,而是放到free_list中,然後我又創建了一個新的對象v1=999,這個對象不會開闢一塊新內存,而是直接從free_list中去獲取對象,然後把對象內部的數據進行初始化成999,再放到refchain中去,需要注意的是,free_list有大小限制,如果free_list鏈表滿了,當一個對象的引用計數為0時,會直接回收這塊地址,而不會放到free_list中進行緩存
float: 維護了free_list長度為100
int:不是基於free_list, 而是維護一個small_list保持常見的數據(小數據池),重複使用不會開闢新的內存
str: 內存將所有的ascii字符緩存起來,以後使用的時候不會反覆創建
list: 維護了free_list長度為80
tuple:根據元素個數來維護free_list長度
dict:維護了free_list長度為80
Python垃圾回收機制是什麼樣的?
Python垃圾回收機制是通過引用計數來管理的引用計數表示記錄這個對象被引用的次數如果有新的引用指向對象,對象引用計數就加一,引用被銷毀時,對象引用計數減一,當用戶的引用計數為0時,該內存被釋放以上就是Python的垃圾回收機制了 ,在黑馬程序員看過一個視頻,有專門講解的,你可以去看看!謝謝你,如果你有這方面的問題的話,您可以隨時詢問我
python垃圾收集機制?
不再使用的內存會被一種稱為垃圾收集的機制釋放。像上面說的,雖然解釋器跟蹤對象的引用計數,但垃圾收集器負責釋放內存。垃圾收集器是一塊獨立代碼,它用來尋找引用計數為0的對象。它也負責檢查那些雖然引用計數大於0但也應該被銷毀的對象。特定情形會導致循環引用。一個循環引用發生在當你有至少兩個對象互相引用時,也就是說所有的引用都消失時,這些引用仍然存在,這說明只靠引用計數是不夠的。Python的垃圾收集器實際上是一個引用計數器和一個循環垃圾收集器。當一個對象的引用計數變為0,解釋器會暫停,釋放掉這個對象和僅有這個對象可訪問(可到達)的其他對象。作為引用計數的補充,垃圾收集器也會留心被分配的總量很大的(及未通過引用計數銷毀的那些)對象。在這種情況下,解釋器會暫停下來,試圖清理所有未引用的循環。
Python的垃圾回收機制原理
1.Python的垃圾回收機制原理
Python無需我們手動回收內存,它的垃圾回收是如何實現的呢?
引用計數為主(缺點:循環引用無法解決)
引入標記清除和分代回收解決引用計數問題
引用計數為主+標記清除和分代回收為輔
垃圾回收(GC)
(1)引用計數
a = [1] # [1]對象引用計數增加1,ref=1
b = a # [1]對象引用計數增加1,ref=2
b = None # [1]對象引用計數減少1,ref=1
del a # [1]對象引用計數減少1,ref=0
a = [1],當把列表 [1] 賦值給 a 的時候,它的引用計數就會增加1,此時列表 [1] 對象的引用計數ref=1 ; b = a 又把 a 賦值給 b ,a和b 同時引用了列表[1]對象,ref又增加1,此時 ref =2。繼續執行 b = None, 讓b指向None,這個時候它就不會指向原來的列表[1]對象,列表[1]對象的引入計數就會減少1,又變成ref=1。執行del a ,引用計數就會減少1,這個時候 ref = 0。當對象的引用計數為0就可以回收掉,
注意:del 作用就會減少對象引用計數,並不是銷毀對象。只有當引用計數為0的時候,Python解釋器才回去把對象佔用的內存回收掉。
// object.h
struct _object {
Py_ssize_t ob_refcnt; # 引用計數值
}PyObject;
① 什麼時候引用計數增加呢?
② 什麼時候引用計數會減少呢?
(2)引用計數無法解決循環引用問題
循環引用
a = [1] # 對象[1]引用計數增加1,ref=1
b = [2] # 對象[2]引用計數增加1,ref=1
a.append(b) # b被a引用,對象[2]引用計數增加1,ref=2
b.append(a) # a又被b引用,對象[1]引用計數增加1,ref=2
del a # 對象[1]引用計數減少1,ref=1
del b # 對象[2]引用計數減少1,ref=1
(3)標記清除(Mark and Sweep)
(4)分代回收
import gc
python中的變量與垃圾回收
python中的變量和java中的變量本質是不一樣的,python中的變量實質上是一個指針(指針的大小固定的)
is可以用來判斷id是否相等
對於這種賦值,雖然所賦值是相同的,但是他們的id不同,即他們是不同的對象,a is b 即為false ,但是有個特例: a = 1 b = 1 時他們的id相同。其實這是python內部的優化機制,對於小整數和小的字符串來說,python在前邊定義一個對象時,下次在遇到時會直接調用前邊生成的對象,而不會去重新申請一個。
他們的對象內存地址不一樣,但是,a和b里的值是相等的,這是由於a和b都為list,而list里有內置的魔法函數 eq 通過 eq 魔法函數可以判斷裡邊兩個的值是否相同,若相同則返回True
python中垃圾回收的算法回收的算法是採用引用計數,當程序中有一個變量引用該python對象時,python會自動保證該對象引用計數為1;當程序中有兩個變量引用該python對象時,python會自動保證該對象計數器為2, 以此類推,當一個對象的引用計數器變為0 時,則說明程序中不再有變量對其進行引用,因此python就會回收該對象。
大多數情況,python的ARC都能準確,高效的回收系統中的每一個對象。但如果系統中出現循環引用時,比如a對象持有一個實例變量引用對象b,而b對象又持有一個實例變量引用對象a,此時 兩個對象的計數器都為1, 而實際上python不再需要這兩個對象,也沒有程序在引用他們,系統回收他們時python的垃圾回收器就沒有那兒快,要等到專門的循環垃圾回收器(Cyclic Garbage Collector)來檢測並回收這種引用循環
當一個對象被垃圾回收式,python就會自動調用該對象的 del 方法
當沒有注釋掉x = im時, item對象被兩個變量所引用,所以在執行完del im時並不會去回收item對象,所以先輸出——–,當程序完全執行完成後,引用item的對象的變量被釋放,然後系統便會執行 del 方法,回收item對象。
當 x = im被注釋後,只有一個變量去引用item對象,所以在執行完後程序變回去調用 del 方法,回收item對象,然後在繼續向下執行 輸出—–
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/253333.html