python内存泄露的原因_python内存泄露排查

本文介绍了如何排查和解决Python内存泄露问题。通过观察资源占用情况,确定问题是由于某个特定请求引起。使用objgraph、pympler和guppy等工具进行内存泄露分析,发现matplotlib.pyplot绘图操作未正确清理导致内存增长。解决方案是在使用matplotlib.pyplot后添加适当的清理操作,如clf()或close(),以释放内存。
摘要由CSDN通过智能技术生成

最近线上某台虚拟机隔三差五就会挂掉,通过业务日志基本上排查到每次出错都源于某一个请求。于是对该请求展开排查。

1,先确认罪魁祸首:

执行该请求之前之前的虚拟机memory和python进程占用的资源:

15905a25164470a221f949cc5844c24b.png

执行一次该请求之后的资源占用情况:

ae48393e7c8e21c6703344e8801cfeb2.png

python占用的资cpu在服务执行过程中会有所提升,但是请求结束后,cpu可以恢复到执行之前的水平;而VIRT,RES,内存占比却有显著提升,且执行完成后并未下降。多次执行,内存占用累积上涨。由此推断,罪魁祸首是该请求,并且可能是由内存泄露引起的。

2,python内存泄露的工具

通过网上一系列的查找,了解到用于排查python内存泄露的工具有:objgraph,pympler,guppy

objgraph

可以查看对象被引用次数的工具,也可以查看对象调用图。

这里主要用到的方法:

show_most_common_types()

show_growth()

个人觉得show_growth更好用写,可以看到增量数据。

b361db2b597d813c7d212f1e863020c7.png

pympler

pympler工具可以很容易看到内存的使用情况

d62f4e733e5f1718f24cf3a7217a7ff6.png

guppy

guppy可以查看到heap内存的具体使用情况,哪些对象占用多少内存

5512060f156b08fcee0ac5914edfe703.png

3,代码定位

先写一个记录对象引用次数的方法

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()

把该方法放在可疑代码前后执行

8b269de46201a0d23decb2b67d92e741.png

fadb0a0764c5609ec639a0a5572ff6ba.png

通过前后执行对比发现引用list,dict等对象均有较大增加。然后对代码进行走读,逐步缩小范围,对可疑代码段进行前后对比,确定最小范围:

f8e0a72a8e1a7cd3ba99dc4e3b592a60.png

6f1ce237f4190886d9ec64583d5c7340.png

同时对象引用的前后对比也佐证了这一点,如上图。该段代码是基于matplotlib.pyplot绘制一个曲线图,对代码主题功能不影响不是很大,我们先把该段代码注释掉,再次执行看对象前后引用次数。

fc6d37290270661e919b30e75e1c5caf.png

通过对比,发现对象引用次数正常了!!!

在服务器部署执行后,对比top信息,执行前:

6815200308d4c46be3935cd4d328fdb9.png

执行中

5262fc2ca186e0a41ce86bda4337d2b4.png

447704062378ac2b788447cf7e2659ae.png

执行后

43fed52d8b06fa7a6dc7e412cd20ed3f.png

执行完成后内存恢复到执行前相当的水平,问题迎刃而解!

4,刨根问底

抓到真凶后,我们总归是好奇,想知道真相的。通过度娘我们发现:

利用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时,需要在后面追加一个释放操作。

db6d517a7ef834287909763795e4295e.png

总体来说这是一次由于经验不足导致的犯错,最终解决方法不复杂,但是重在问题排查的过程和方法,学到了很多。

文章来源: segmentfault.com,作者:ChristmasBoy,版权归原作者所有,如需转载,请联系作者。

原文链接:segmentfault.com/a/1190000038277797

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值