一、前言 Python 是一門高級語言,使用起來類似於自然語言,開發的時候自然十分方便快捷,原因是Python在背後為我們默默做了很多事情,其中一件就是垃圾回收,來解決記憶體管理,記憶體泄漏的問題。 記憶體泄漏:當程式不停運行,有一部分對象沒有作用,但所占記憶體沒有被釋放,伺服器記憶體隨時間越來越少,最終導致 ...
一、前言
Python 是一門高級語言,使用起來類似於自然語言,開發的時候自然十分方便快捷,原因是Python在背後為我們默默做了很多事情,其中一件就是垃圾回收,來解決記憶體管理,記憶體泄漏的問題。
記憶體泄漏:當程式不停運行,有一部分對象沒有作用,但所占記憶體沒有被釋放,伺服器記憶體隨時間越來越少,最終導致系統的崩潰,所以記憶體泄漏是一個需要重點關註的問題。
二、引用計數
Python 標記一個對象是否還有用的方法就是用引用計數,以下情形會為該對象的計數+1:
1. 創建時
2. 被引用時
3. 作為參數傳入函數時
相反,以下情形會為該對象的計數-1:
1. 被del
2. 被重引用
3. 函數執行完畢
查看某一元素的計數可以通過 sys.getrefcount(),當引用計數為0 的時候,記憶體就會被釋放。
可以想到和其他垃圾回收相比,Python的機制優點很明顯,就是實時性,Python的gc 模塊就是開放的介面用以管理。
也可以很容易猜到這樣的缺點就是性能相對較低,看過這樣的報道,instagram 通過禁用 gc 模塊,性能提升10%!
三、 迴圈引用
有一種特殊情況,當兩個或多個變數互相迴圈引用的時候,按照計數引用的機制就無法處理了
a = [] b = [] a.append(b) b.append(a) print(a,b)
a,b 的引用計數均為2,無法回收兩者記憶體
四、解決方案
1. 通過 ”標記-清除“ 來解決迴圈調用問題:
垃圾回收器定時去尋找這類迴圈調用,並清除
具體是 先從 根對象集合副本中 開始尋找,這些對象計數不為0,沒有被清除
然後一個個檢測,將其分為可達對象和不可達對象,底層通過鏈表的數據結構實現,通過操作副本清除標記,來在不影響原數據的情況下,判斷是否為迴圈調用
最後將不可達對象清除,釋放記憶體,效率較低。
有三種情況會觸發垃圾回收:
1.調用gc.collect()
,
2.當gc模塊的計數器達到閥值的時候。
3.程式退出的時候
2.分代回收,利用 “空間換時間”策略提高效率:
有些記憶體塊生存時間從開始到結束,有些則很短,所以同樣對他們進行垃圾回收是很浪費的一件事情,
所有對象開始被劃分到零代中,Python 預設 有三代,一個代就是一個鏈表
年輕代中的對象優先處理,經歷垃圾處理次數愈多的,越“老資格” ,就會上升,最終放在第二代中。
備註:
Python的垃圾回收機制是通過檢測數量是否到達閾值來決定是否進行。
Python 這方面源碼是c寫的,暫時看不懂,留待以後搞懂鏈表結構再來研究,
gc 模塊 留待以後研究。