最近线上某台虚拟机隔三差五就会挂掉,经过业务日志基本上排查到每次出错都源于某一个请求。因而对该请求展开排查。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时,须要在后面追加一个释放操做。
整体来讲这是一次因为经验不足致使的犯错,最终解决方法不复杂,可是重在问题排查的过程和方法,学到了不少。