問題描述
調試python程序時,用下面這段代碼,可以獲得進程占用系統內存值。程序跑一段時間后,就能畫出進程對內存的占用情況。
def memory_usage_psutil(): # return the memory usage in MB import psutil,os process = psutil.Process(os.getpid()) mem = process.memory_info()[0] / float(2 ** 20) return mem
發現進程的內存占用一直再上漲,而這從邏輯上來說是不正常的,所以想到程序可能發生了Memory Leak。
python程序的Mem Leak
python程序不可能像C/C++一樣出現malloc了的內存沒有free這樣的Memory Leak。但也會遇到“邏輯上沒free”的情況,如下代碼所示。
def foo(a=[]): a.append(time.time()) return a
參數a這樣可迭代的對象,稍不注意,它就能增長的很快。說白了,python的Memory Leak,就是“進程占用的內存莫名其妙一直再升高”。進程占用內存一直升高,與邏輯預期不一致,就可能發生了Memory Leak。
以下面程序為例說明Memory Leak調試的過程:
def memory_usage_psutil(): # return the memory usage in MB import psutil,os process = psutil.Process(os.getpid()) mem = process.memory_info()[0] / float(2 ** 20) return memdef get_current_obj(a=[]): a.append([0]*1000) return adef main(): obj = [] for i in range(10000): obj = get_current_obj(obj) if(i%100==0): print(memory_usage_psutil())if __name__=='__main__': main()
調試過程
用pmap -x [pid]查看進程占用的堆內存大小
首先想到,會不會是上面用的memory_usage_psutil函數統計錯誤呢。
先運行程序,再用pmap查看,發現進程內存占用確實很高。多次執行該命令,也可以發現內存一直升高。
強制執行GC(gc.collect())
在需要執行GC的地方加上gc.collect()
def main(): obj = [] for i in range(10000): obj = get_current_obj(obj) import gc;gc.collect() if(i%100==0): print(memory_usage_psutil())
可以看到,強制GC后,程序執行變慢,但內存依然不斷升高。
使用memory_profiler查看
安裝memory_profiler
pip install -U memory_profiler
用@profile修飾需要查看內存的函數
@profiledef main(): obj = [] for i in range(10000): obj = get_current_obj(obj) if(i%100==0): print(memory_usage_psutil())
用如下命令運行程序
python -m memory_profiler main.py
可以看到程序執行完成后,輸出結果如下
Line # Mem usage Increment Line Contents================================================ 12 28.570 MiB 0.000 MiB @profile 13 def main(): 14 28.570 MiB 0.000 MiB obj = [] 15 106.203 MiB 77.633 MiB for i in range(10000): 16 106.203 MiB 0.000 MiB obj = get_current_obj(obj) 17 106.203 MiB 0.000 MiB if(i%100==0): 18 105.445 MiB -0.758 MiB print(memory_usage_psutil())
新聞熱點
疑難解答