那麼,對於不會再用到的記憶體空間,Python 是通過什麼機制來管理的呢?其實在前面章節已大致接觸過,就是參照計數機制。這裡的記憶體漏失是指程式本身沒有設計好,導致程式未能釋放已不再使用的記憶體,或者直接失去了對某段記憶體的控制,造成了記憶體的浪費。
import os import psutil # 顯示當前 python 程式佔用的記憶體大小 def show_memory_info(hint): pid = os.getpid() p = psutil.Process(pid) info = p.memory_full_info() memory = info.uss / 1024. / 1024 print('{} memory used: {} MB'.format(hint, memory)) def func(): show_memory_info('initial') a = [i for i in range(10000000)] show_memory_info('after a created') func() show_memory_info('finished')輸出結果為:
initial memory used: 47.19140625 MB
after a created memory used: 433.91015625 MB
finished memory used: 48.109375 MB
def func(): show_memory_info('initial') global a a = [i for i in range(10000000)] show_memory_info('after a created') func() show_memory_info('finished')輸出結果為:
initial memory used: 48.88671875 MB
after a created memory used: 433.94921875 MB
finished memory used: 433.94921875 MB
def func(): show_memory_info('initial') a = [i for i in derange(10000000)] show_memory_info('after a created') return a a = func() show_memory_info('finished')輸出結果為:
initial memory used: 47.96484375 MB
after a created memory used: 434.515625 MB
finished memory used: 434.515625 MB
import sys a = [] # 兩次參照,一次來自 a,一次來自 getrefcount print(sys.getrefcount(a)) def func(a): # 四次參照,a,python 的函數呼叫棧,函數引數,和 getrefcount print(sys.getrefcount(a)) func(a) # 兩次參照,一次來自 a,一次來自 getrefcount,函數 func 呼叫已經不存在 print(sys.getrefcount(a))輸出結果為:
2
4
2
另一個要注意的是,在函數呼叫發生的時候,會產生額外的兩次參照,一次來自函數棧,另一個是函數引數。注意,sys.getrefcount() 函數用於檢視一個變數的參照次數,不過別忘了,getrefcount 本身也會引入一次計數。
import sys a = [] print(sys.getrefcount(a)) # 兩次 b = a print(sys.getrefcount(a)) # 三次 c = b d = b e = c f = e g = d print(sys.getrefcount(a)) # 八次輸出結果為:
2
3
8
import gc show_memory_info('initial') a = [i for i in range(10000000)] show_memory_info('after a created') del a gc.collect() show_memory_info('finish') print(a)輸出結果為:
initial memory used: 48.1015625 MB
after a created memory used: 434.3828125 MB
finish memory used: 48.33203125 MB
NameError Traceback (most recent call last)
<ipython-input-12-153e15063d8a> in <module>
11
12 show_memory_info('finish')
---> 13 print(a)
NameError: name 'a' is not defined
def func(): show_memory_info('initial') a = [i for i in range(10000000)] b = [i for i in range(10000000)] show_memory_info('after a, b created') a.append(b) b.append(a) func() show_memory_info('finished')輸出結果為:
initial memory used: 47.984375 MB
after a, b created memory used: 822.73828125 MB
finished memory used: 821.73046875 MB
import gc def func(): show_memory_info('initial') a = [i for i in range(10000000)] b = [i for i in range(10000000)] show_memory_info('after a, b created') a.append(b) b.append(a) func() gc.collect() show_memory_info('finished')輸出結果為:
initial memory used: 49.51171875 MB
after a, b created memory used: 824.1328125 MB
finished memory used: 49.98046875 MB
由於篇幅有限,這裡不再對標記清除演算法和分代收集演算法的具體實現所詳細介紹,有興趣的讀者可自行百度搜尋。