一.垃圾回收機制
Python中的垃圾回收是以引用數為主,分代收集為輔。引用計數的缺陷是循環引用的問題。
在Python中,如果一個物件的參考數為0,Python虛擬機器就會回收這個物件的記憶體。
#encoding=utf-8 __author__ = 'kevinlu1010@qq.com' class ClassA(): def __init__(self): print 'object born,id:%s'%str(hex(id(self))) def __del__(self): print 'object del,id:%s'%str(hex(id(self))) def f1(): while True: c1=ClassA() del c1
執行f1()會循環輸出這樣的結果,而且進程佔用的記憶體基本上不會變動
object born,id:0x237cf58 object del,id:0x237cf58
c1=ClassA()會建立一個對象,放在0x237cf58內存中,c1變數指向這個內存,這時候這個內存的引用計數是1
del c1後,c1變量不再指向0x237cf58內存,所以這塊內存的引用計數減一,等於0,所以就銷毀了這個對象,然後釋放記憶體。
導致引用計數+1的情況
物件被創建,例如a=23
物件被引用,例如b=a
物件被作為參數,傳入到一個函數中,例如func(a )
物件作為一個元素,儲存在容器中,例如list1=[a,a]
導致引用計數-1的情況
物件的別名被明確銷毀,例如del a
物件的別名被賦予新的對象,例如a=24
一個物件離開它的作用域,例如f函數執行完畢時,func函數中的局部變數(全域變數不會)
物件所在的容器被銷毀,或從容器中刪除物件
demo
def func(c,d): print 'in func function', sys.getrefcount(c) - 1 print 'init', sys.getrefcount(11) - 1 a = 11 print 'after a=11', sys.getrefcount(11) - 1 b = a print 'after b=1', sys.getrefcount(11) - 1 func(11) print 'after func(a)', sys.getrefcount(11) - 1 list1 = [a, 12, 14] print 'after list1=[a,12,14]', sys.getrefcount(11) - 1 a=12 print 'after a=12', sys.getrefcount(11) - 1 del a print 'after del a', sys.getrefcount(11) - 1 del b print 'after del b', sys.getrefcount(11) - 1 # list1.pop(0) # print 'after pop list1',sys.getrefcount(11)-1 del list1 print 'after del list1', sys.getrefcount(11) - 1
輸出:
init 24 after a=11 25 after b=1 26 in func function 28 after func(a) 26 after list1=[a,12,14] 27 after a=12 26 after del a 26 after del b 25 after del list1 24
問題:為什麼呼叫函數會令引用計數+2
檢視一個對象的參考計數
sys.getrefcount(a)可以查看a物件的參考計數,但是比正常計數大1,因為呼叫函數的時候傳入a,這會讓a的引用計數+1
二.循環引用導致記憶體外洩
def f2(): while True: c1=ClassA() c2=ClassA() c1.t=c2 c2.t=c1 del c1 del c2
執行f2(),進程佔用的記憶體會不斷增加。
object born,id:0x237cf30 object born,id:0x237cf58
創建了c1,c2後,0x237cf30(c1對應的內存,記為內存1),0x237cf58(c2對應的內存,記為內存2)這兩塊內存的引用計數都是1,執行c1.t=c2和c2.t=c1後,這兩塊內存的引用計數變成2.
在del c1後,內存1的對象的引用計數變為1,由於不是為0,所以內存1的對像不會被銷毀,所以內存2的對象的引用數仍然是2,在del c2後,同理,內存1的對象,內存2的對象的引用數都是1。
雖然它們兩個的物件都是可以被銷毀的,但是由於循環引用,導致垃圾回收器都不會回收它們,所以就會導致記憶體洩漏。
三.垃圾回收
deff3(): # print gc.collect() c1=ClassA() c2=ClassA() c1.t=c2 c2.t=c1 del c1 del c2 print gc.garbage print gc.collect() #显式执行垃圾回收 print gc.garbage time.sleep(10) if __name__ == '__main__': gc.set_debug(gc.DEBUG_LEAK) #设置gc模块的日志 f3()
輸出:
Python
gc: uncollectable <classa> gc: uncollectable <classa> gc: uncollectable <dict> gc: uncollectable <dict> object born,id:0x230e918 object born,id:0x230e940</dict></dict></classa></classa>
#垃圾回收後的物件會放在gc.garbage列表裡面
gc.collect()會傳回不可達的物件數目,4等於兩個物件以及它們對應的dict
有三種情況會觸發垃圾回收:
1.呼叫gc .collect(),
2.當gc模組的計數器達到閥值的時候。
3.程式退出的時候
四.gc模組常用功能解析
Garbage Collector interface
gc模組提供一個介面給開發者設定垃圾回收的選項。上面說到,採用引用計數的方法管理記憶體的一個缺陷是循環引用,而gc模組的一個主要功能就是解決循環引用的問題。
常用函數:
gc.set_debug(flags)
設定gc的debug日誌,一般設定為gc.DEBUG_LEAK
gc.collect([generation])
明確進行垃圾回收,可以輸入參數,0代表只檢查第一代的對象,1代表檢查一,二代的對象,2代表檢查一,二,三代的對象,如果不傳參數,執行一個full collection,也就是等於傳2 。
傳回不可達(unreachable objects)物件的數目
gc.set_threshold(threshold0[, threshold1[, threshold2])
設定自動執行垃圾回收的頻率。
gc.get_count()
取得目前自動執行垃圾回收的計數器,傳回長度為3的清單
gc模組的自動垃圾回收機制
必須要import gc模組,並且is_enable() =True才會啟動自動垃圾回收。
這個機制的主要功能就是發現並處理不可達的垃圾物件。
垃圾回收=垃圾檢查+垃圾回收
在Python中,採用分代收集的方法。把物件分為三代,一開始,物件在創建的時候,放在一代中,如果在一次一代的垃圾檢查中,改對象存活下來,就會被放到二代中,同理在一次二代的在垃圾檢查中,該物件存活下來,就會被放到三代中。
gc模組裡面會有一個長度為3的列表的計數器,可以透過gc.get_count()取得。
例如(488,3,0),其中488是指距離上一次一代垃圾檢查,Python分配記憶體的數目減去釋放記憶體的數目,注意是記憶體分配,而不是引用計數的增加。例如:
print gc.get_count() # (590, 8, 0) a = ClassA() print gc.get_count() # (591, 8, 0) del a print gc.get_count() # (590, 8, 0)
3是指距离上一次二代垃圾检查,一代垃圾检查的次数,同理,0是指距离上一次三代垃圾检查,二代垃圾检查的次数。
gc模快有一个自动垃圾回收的阀值,即通过gc.get_threshold函数获取到的长度为3的元组,例如(700,10,10)
每一次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数目,如果是,就会执行对应的代数的垃圾检查,然后重置计数器
例如,假设阀值是(700,10,10):
当计数器从(699,3,0)增加到(700,3,0),gc模块就会执行gc.collect(0),即检查一代对象的垃圾,并重置计数器为(0,4,0)
当计数器从(699,9,0)增加到(700,9,0),gc模块就会执行gc.collect(1),即检查一、二代对象的垃圾,并重置计数器为(0,0,1)
当计数器从(699,9,9)增加到(700,9,9),gc模块就会执行gc.collect(2),即检查一、二、三代对象的垃圾,并重置计数器为(0,0,0)
其他
如果循环引用中,两个对象都定义了__del__方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的__del__方法,所以为了安全起见,gc模块会把对象放到gc.garbage中,但是不会销毁对象。
五.应用
项目中避免循环引用
引入gc模块,启动gc模块的自动清理循环引用的对象机制
由于分代收集,所以把需要长期使用的变量集中管理,并尽快移到二代以后,减少GC检查时的消耗
gc模块唯一处理不了的是循环引用的类都有__del__方法,所以项目中要避免定义__del__方法,如果一定要使用该方法,同时导致了循环引用,需要代码显式调用gc.garbage里面的对象的__del__来打破僵局
以上是詳解Python的垃圾回收機制方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Linux終端中查看Python版本時遇到權限問題的解決方法當你在Linux終端中嘗試查看Python的版本時,輸入python...

本文解釋瞭如何使用美麗的湯庫來解析html。 它詳細介紹了常見方法,例如find(),find_all(),select()和get_text(),以用於數據提取,處理不同的HTML結構和錯誤以及替代方案(SEL)

本文比較了Tensorflow和Pytorch的深度學習。 它詳細介紹了所涉及的步驟:數據準備,模型構建,培訓,評估和部署。 框架之間的關鍵差異,特別是關於計算刻度的

Python的statistics模塊提供強大的數據統計分析功能,幫助我們快速理解數據整體特徵,例如生物統計學和商業分析等領域。無需逐個查看數據點,只需查看均值或方差等統計量,即可發現原始數據中可能被忽略的趨勢和特徵,並更輕鬆、有效地比較大型數據集。 本教程將介紹如何計算平均值和衡量數據集的離散程度。除非另有說明,本模塊中的所有函數都支持使用mean()函數計算平均值,而非簡單的求和平均。 也可使用浮點數。 import random import statistics from fracti

本文指導Python開發人員構建命令行界面(CLIS)。 它使用Typer,Click和ArgParse等庫詳細介紹,強調輸入/輸出處理,並促進用戶友好的設計模式,以提高CLI可用性。

在使用Python的pandas庫時,如何在兩個結構不同的DataFrame之間進行整列複製是一個常見的問題。假設我們有兩個Dat...

本文討論了諸如Numpy,Pandas,Matplotlib,Scikit-Learn,Tensorflow,Tensorflow,Django,Blask和請求等流行的Python庫,並詳細介紹了它們在科學計算,數據分析,可視化,機器學習,網絡開發和H中的用途

文章討論了虛擬環境在Python中的作用,重點是管理項目依賴性並避免衝突。它詳細介紹了他們在改善項目管理和減少依賴問題方面的創建,激活和利益。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3 Linux新版
SublimeText3 Linux最新版

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),