淺析Python垃圾回收機制!
阿新 • • 發佈:2020-07-09
Python垃圾回收機制
目錄
1. 記憶體洩露
- 程式本身沒有設計好,導致程式未能釋放已不再使用的記憶體
- 程式碼在分配了某段記憶體後,因為設計錯誤,失去了對這段記憶體的控制,從而造成了記憶體的浪費
監控python程式記憶體佔用情況,psutil庫
import os import psutil # 顯示當前程式佔用記憶體大小 def show_memory_info(hint): pid = os.getpid() # 獲取當前程序號 p = psutil.Process(pid) info = p.memory_info() memory = info.rss / 1024. / 1024 print(f"{hint} 記憶體用了:{memory}MB")
檢視物件佔用記憶體大小
import sys
a = [i for i in range(10000)]
memory = sys.getsizeof(a) / 1024
print(f"記憶體用了:{memory}KB")
# 記憶體用了:85.578125KB
2. Python什麼時候啟動垃圾回收機制?
2.1 計數引用
python中一切皆物件,所看到的一切變數,本質上都是物件的一個指標,當這個物件的引用次數為0的時候,說明這個物件永不可達,成為需要被回收的垃圾
# 檢視引用次數 import sys a = [] print(sys.getrefcount(a)) # 兩次引用,一次a,一次getr def func(a): # 四次引用,函式呼叫會產生兩次額外的引用,一次來自函式棧,另一個是函式引數 print(sys.getrefcount(a)) func(a)
2
4
def func():
show_memory_info('初始')
a = [i for i in range(1000000)] # 列表生成式
show_memory_info('建立之後')
func()
show_memory_info('結束之後') # 記憶體即可被釋放
初始 記憶體用了:52.4609375MB
建立之後 記憶體用了:91.921875MB
結束之後 記憶體用了:53.80859375MB
2.2 迴圈引用
- python中使用標記清除演算法和分代收集,來啟動針對迴圈引用的自動垃圾回收
- 標記清除演算法,圖論中的不可達概念
- 分代收集演算法中每一代都有一個預設閾值,超過指定閾值之後就會啟動垃圾回收,如果垃圾回收啟動太頻繁,會造成程式效能低下,分代收集為了提高效能,因此不立刻回收。
def func():
show_memory_info('初始')
a = [i for i in range(1000000)] # 列表生成式
b = [i for i in range(1000000)] # 列表生成式
show_memory_info('建立之後')
a.append(b)
b.append(a)
func()
show_memory_info('結束之後') # 可以看到迴圈引用之後,記憶體依舊被佔用
初始 記憶體用了:77.125MB
建立之後 記憶體用了:163.8828125MB
結束之後 記憶體用了:163.8828125MB
顯示呼叫gc.collect()來啟動垃圾回收
import gc
def func():
show_memory_info('初始')
a = [i for i in range(1000000)] # 列表生成式
b = [i for i in range(1000000)] # 列表生成式
show_memory_info('建立之後')
a.append(b)
b.append(a)
func()
# 顯示呼叫gc.collect()來啟動垃圾回收
gc.collect()
show_memory_info('結束之後')
初始 記憶體用了:77.609375MB
建立之後 記憶體用了:145.92578125MB
結束之後 記憶體用了:77.609375MB
問題:引用計數是0是啟動垃圾回收的充要條件嗎?
引用計數是其中最簡單的實現,不是充要條件,只能算作充分非必要條件,迴圈引用需要通過不可達判定,來確定是否可以回收。python中自動回收演算法包括標記清除演算法和分代收集。