最近線上某臺虛擬機隔三差五就會掛掉,經過業務日誌基本上排查到每次出錯都源於某一個請求。因而對該請求展開排查。html
執行該請求以前以前的虛擬機memory和python進程佔用的資源:python
執行一次該請求以後的資源佔用狀況:git
python佔用的資cpu在服務執行過程當中會有所提高,可是請求結束後,cpu能夠恢復到執行以前的水平;而VIRT,RES,內存佔比卻有顯著提高,且執行完成後並未降低。屢次執行,內存佔用累積上漲。由此推斷,罪魁禍首是該請求,而且多是由內存泄露引發的。github
https://zhmin.github.io/2018/12/22/python-meomory-leak/api
經過網上一系列的查找,瞭解到用於排查python內存泄露的工具備:objgraph,pympler,guppy服務器
objgraph函數
能夠查看對象被引用次數的工具,也能夠查看對象調用圖。工具
這裏主要用到的方法:spa
show_most_common_types().net
show_growth()
我的以爲show_growth更好用寫,能夠看到增量數據。
pympler
pympler工具能夠很容易看到內存的使用狀況
guppy
guppy能夠查看到heap內存的具體使用狀況,哪些對象佔用多少內存
先寫一個記錄對象引用次數的方法
import os import objgraph def obj_graph_stat(mark=''): file_path = r'D:\obj_graph.txt' if not os.path.exists(file_path): file = open(file_path, 'w') file.close() file = open(file_path, 'a') file.write(f'******************{str(now_datetime())}-{mark}******************\n') objgraph.show_most_common_types(limit=20, file=file) file.write(f'-'*20) file.write('\n') # 返回heap內存詳情 # heap = hp.heap() # byvia返回該對象的被哪些引用, heap[0]是內存消耗最大的對象 # references = heap[0].byvia # file.write(str(references)) file.write('\n\n') file.close()
把該方法放在可疑代碼先後執行
經過先後執行對比發現引用list,dict等對象均有較大增長。而後對代碼進行走讀,逐步縮小範圍,對可疑代碼段進行先後對比,肯定最小範圍:
同時對象引用的先後對比也佐證了這一點,如上圖。該段代碼是基於matplotlib.pyplot繪製一個曲線圖,對代碼主題功能不影響不是很大,咱們先把該段代碼註釋掉,再次執行看對象先後引用次數。
經過對比,發現對象引用次數正常了!!!
在服務器部署執行後,對比top信息,執行前:
執行中
執行後
執行完成後內存恢復到執行前至關的水平,問題迎刃而解!
抓到真兇後,咱們總歸是好奇,想知道真相的。經過度娘咱們發現:
Python循環畫圖時內存泄露的問題:http://www.biexiaoyu1994.com/%E4%BB%A3%E7%A0%81%E8%B8%A9%E5%9D%91/2019/06/13/python_plot_mem_leak/
matplotlib畫圖內存爆表:https://blog.csdn.net/quanshengxixin/article/details/68953314
matplotlib內存溢出報錯:https://blog.csdn.net/mym_74/article/details/102887252
利用matplotlib繪製圖片,而且將圖片保存到文件中。由於沒有及時的將內存中的圖像清除,導致內存爆表,系統卡死。pyplot是一個模塊,它收集了一些容許matplotlib以功能方式使用的函數。 我在這裏假設pyplot已被導入爲「import matplotlib.pyplot as plt」。 在這種狀況下,有三個不一樣的命令能夠刪除內容:
plt.cla()清除軸,當前活動軸在當前圖中。 它保持其餘軸不變。
plt.clf()清除整個當前數字。與全部的軸,但離開窗口打開,這樣它就能夠再用在其餘的 plots上了。
plt.close()關上窗戶,若是未另指定,則該窗口將是當前窗口。
所以,哪一種功能最適合您,取決於您的用例。
close()函數還容許指定哪一個窗口應該關閉。參數能夠是使用figure(number_or_name)建立的窗口的數字或名稱。也能夠是得到的圖形實例,即便用fig = figure()。若是沒有人提出任何論點close(),當前活動的窗口將關閉。 此外,還有語法close('all'),它關閉全部數字。
總結經驗,也就是咱們在使用matplotlib.pyplot時,須要在後面追加一個釋放操做。
整體來講這是一次因爲經驗不足致使的犯錯,最終解決方法不復雜,可是重在問題排查的過程和方法,學到了不少。