记一次python 内存泄漏问题及解决过程


Posted in Python onNovember 29, 2018

最近工作中慢慢开始用python协程相关的东西,所以用到了一些相关模块,如aiohttp, aiomysql, aioredis等,用的过程中也碰到的很多问题,这里整理了一次内存泄漏的问题

通常我们写python程序的时候也很少关注内存这个问题(当然可能我的能力还有待提升),可能写c和c++的朋友会更多的考虑这个问题,但是一旦我们的python程序出现了

内存泄漏的问题,也将是一件非常麻烦的事情了,而最近的一次代码中也碰到了这个问题,不过好在最后内存溢出不是我代码的问题,而是所用到的一个包出现了内存的问题,下面我通过一个简单的代码模拟出内存的问题,然后也会将解决的过程描述一下,希望能帮助到遇到同样问题的朋友。

一、复现问题

其实这次主要是在使用aiohttp写一个接口的时候出现的问题,其实复现出问题非常容易,我们实现一个简单的接受post请求接口的服务端,然后实现一个并发的客户端来访问这个接口,来查看内存的情况

注意: 这个问题是在一个包的特定版本出现的:multidict==4.5.1,我在整理这个文章2个小时前作者已经修复了这个问题发布了4.5.2版本,已经修复了内存的问题,并且我也进行了测试验证

服务端代码:

from aiohttp import web
async def hello(request):
 return web.json_response(await request.json())
app = web.Application()
app.add_routes([web.post('/', hello)])
web.run_app(app)

客户端代码:

import asyncio
import aiohttp
async def foo(times):
 data = {'foo': 1}
 async with aiohttp.ClientSession() as session:
  for x in range(times):
   resp = await session.post('http://localhost:8080', json=data)
   if not x % 100:
    print(await resp.json())
loop = asyncio.get_event_loop()
loop.run_until_complete(foo(100000))
loop.close()

因为我的代码是在linux上跑的,或者mac上我们都可以通过htop非常方面的实时查看我们程序内存的占用情况,我们先将服务端启动,查看一下我们此时的内存情况可以看到占用的

非常少,当我们打开客户端之后,再次观察我们可以看到内存不断增长,及时我们客户端运行完毕内存也不会降低。

记一次python 内存泄漏问题及解决过程

 当客户端结束之后的内存:

如果客户端不停止的话内存会一直涨,最后的结果就是把你的系统内存吃完,然后被系统杀掉你的进程。

记一次python 内存泄漏问题及解决过程

二、解决内存泄漏的过程

像上面的例子是一个非常简单的程序,不复杂我们也并没有做上面复杂的操作就是一个简单的接受post请求的服务端,但是如果是在实际的项目中我们可能会写非常复杂的业务逻辑,那到时候我们又如何找到是哪里导致的内存问题,当我碰到这个问题的时候,其实我和很多接触python不久的人差不多,也是不知道怎么查这种问题,各种百度各种查,也找到了好多推荐的工具,memory_profiler库,objgraph库,graphviz工具,但是都没有帮助我迅速的找到问题点在哪里,最后看到标准库中的tracemalloc,地址:https://docs.python.org/3/library/tracemalloc.html

通过这个包很快帮我找到了内存泄漏的地方

接下来按照官网的方法我将代码进行改写,来测试到底哪里的问题导致的内存泄漏,更改后的服务端代码为:

from aiohttp import web
import tracemalloc
async def hello(request):
 return web.json_response(await request.json())
async def get_info(request):
 snapshot2 = tracemalloc.take_snapshot()
 top_stats = snapshot2.compare_to(snapshot1, 'lineno')
 print(top_stats)
 return web.Response(text="ok")
if __name__ == '__main__':
 app = web.Application()
 app.add_routes(
  [
   web.post('/', hello),
   web.get("/get_info", get_info)
  ]
 )
 tracemalloc.start()
 snapshot1 = tracemalloc.take_snapshot()
 web.run_app(app)

注意print(top_stats)这行打印的结果最后要关注

 其实这里就是新增加了一个路由get_info, 我们启动服务端之后开启客户端,当我们客户端运行完毕之后,可以看到内存已经涨上去了,并且没有不会释放,这个时候,可以直接通过浏览器访问get_info这个路由看看print打印的内容,这里将会打印出你程序运行到这个时候那一行的代码内存增长的比较多,进行一次排序,前面的几个其实都是需要你关注的,因为这里数据较多,我就只打印如下前几个数据

<StatisticDiff traceback=<Traceback (<Frame filename='/Users/zhaofan/anaconda3/lib/python3.6/site-packages/aiohttp/web_response.py' lineno=56>,)> size=116500672 (+116500672) count=300004 (+300004)>,
<StatisticDiff traceback=<Traceback (<Frame filename='/Users/zhaofan/anaconda3/lib/python3.6/site-packages/aiohttp/web_response.py' lineno=604>,)> size=11400000 (+11400000) count=200000 (+200000)>, 
<StatisticDiff traceback=<Traceback (<Frame filename='/Users/zhaofan/anaconda3/lib/python3.6/site-packages/aiohttp/web_response.py' lineno=472>,)> size=8000000 (+8000000) count=100000 (+100000)>, 
<StatisticDiff traceback=<Traceback (<Frame filename='/Users/zhaofan/anaconda3/lib/python3.6/site-packages/aiohttp/web_response.py' lineno=353>,)> size=5500000 (+5500000) count=100000 (+100000)>, 
<StatisticDiff traceback=<Traceback (<Frame filename='/Users/zhaofan/anaconda3/lib/python3.6/site-packages/aiohttp/web_response.py' lineno=352>,)> size=5300608 (+5300608) count=100001 (+100001)>,

我们拿第一行来说,我们可以非常清楚的指导web_response的56行代码导致内存增长的最多,当然如果是我们复杂的项目也可以通过类似的方法,这样就可以非常快捷的找到我们代码中哪些地方会造成内存溢出,便于排查问题,我们点进去看看这行代码:

记一次python 内存泄漏问题及解决过程

我们找到最终行,这个时候我们大致就可以看出哪里的问题了,我们接着看  CIMultiDict

class CIMultiDict(MultiDict):
 def _title(self, key):
  return key.title()

我们可以看到这个它继承  MultiDict 其实这里我们已经应该知道问题就是处在这个MultiDict上了

而这个最终其实最终就是MultiDict这个包,问题出在了这个包上,这个项目是在这里维护的:https://github.com/aio-libs/multidict

查看这个包的时候看到了,果然有人和我遇到了同样的问题,问题就是出在这里了,已经有人提交了bug

https://github.com/aio-libs/multidict/issues/307

不过不得不说国外的程序员真的是热爱自己的职业,很快这个问题得到了aio-libs小组中人的回应,问题也在我整理这个博客的时候被修复了,在最新的版本:4.5.2中已经测试没有内存泄漏的问题

三、总结

在这里处理的过程中,其实发现了自己很多的不足,查找问题的方式,以及遇到这种问题的解决思路,不过经过这次,至少下次遇到同样的问题,自己能很快的去查找

以及解决问题,还有就是针对https://docs.python.org/3/library/tracemalloc.html这个库的使用,也推荐大家多了解一下。

以上所述是小编给大家介绍的记一次python 内存泄漏问题及解决过程,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持

Python 相关文章推荐
Python中的rfind()方法使用详解
May 19 Python
Python中zfill()方法的使用教程
May 20 Python
Python中Class类用法实例分析
Nov 12 Python
详解Python中的相对导入和绝对导入
Jan 06 Python
Python将多份excel表格整理成一份表格
Jan 03 Python
python装饰器深入学习
Apr 06 Python
python实现txt文件格式转换为arff格式
May 31 Python
对python创建及引用动态变量名的示例讲解
Nov 10 Python
Django Form 实时从数据库中获取数据的操作方法
Jul 25 Python
python中图像通道分离与合并实例
Jan 17 Python
pytorch中的weight-initilzation用法
Jun 24 Python
自定义Django_rest_framework_jwt登陆错误返回的解决
Oct 18 Python
对python pandas 画移动平均线的方法详解
Nov 28 #Python
对pandas中时间窗函数rolling的使用详解
Nov 28 #Python
python 列表递归求和、计数、求最大元素的实例
Nov 28 #Python
使用python对文件中的数值进行累加的实例
Nov 28 #Python
python的concat等多种用法详解
Nov 28 #Python
CentOS下Python3的安装及创建虚拟环境的方法
Nov 28 #Python
python dataframe向下向上填充,fillna和ffill的方法
Nov 28 #Python
You might like
PHP的FTP学习(二)
2006/10/09 PHP
11个PHP 分页脚本推荐
2011/08/15 PHP
Swoole-1.7.22 版本已发布,修复PHP7相关问题
2015/12/31 PHP
php进程间通讯实例分析
2016/07/11 PHP
php 的反射详解及示例代码
2016/08/25 PHP
PHP实现大数(浮点数)取余的方法
2017/02/18 PHP
extjs 学习笔记(一) 一些基础知识
2009/10/13 Javascript
Javascript学习笔记5 类和对象
2010/01/11 Javascript
JQuery为textarea添加maxlength属性的代码
2010/04/07 Javascript
整理一些JavaScript的IE和火狐的兼容性注意事项
2011/03/17 Javascript
jQuery+CSS实现菜单滑动伸展收缩(仿淘宝)
2013/03/22 Javascript
用javascript添加控件自定义属性解析
2013/11/25 Javascript
JavaScript实现班级随机点名小应用需求的具体分析
2014/05/12 Javascript
jQuery实现鼠标划过展示大图的方法
2015/03/09 Javascript
AngularJS 单元测试(二)详解
2016/09/21 Javascript
js实现文字无缝向上滚动
2017/02/16 Javascript
JS触摸与手势事件详解
2017/05/09 Javascript
JS数组交集、并集、差集的示例代码
2017/08/23 Javascript
微信小程序checkbox组件使用详解
2018/01/31 Javascript
如何测量vue应用运行时的性能
2019/06/21 Javascript
Vue插件之滑动验证码用法详解
2020/04/05 Javascript
基于JavaScript实现控制下拉列表
2020/05/08 Javascript
ssm+vue前后端分离框架整合实现(附源码)
2020/07/08 Javascript
Unicode和Python的中文处理
2017/03/19 Python
windows系统下Python环境搭建教程
2017/03/28 Python
Python数字图像处理之霍夫线变换实现详解
2018/01/12 Python
python实现抽奖小程序
2020/04/15 Python
python config文件的读写操作示例
2019/09/27 Python
python装饰器原理与用法深入详解
2019/12/19 Python
python 实现将Numpy数组保存为图像
2020/01/09 Python
会计专业求职信范文
2014/03/16 职场文书
助学贷款贫困证明
2014/09/23 职场文书
工作犯错保证书
2015/05/11 职场文书
导游词之吉林吉塔
2019/11/11 职场文书
一文搞懂如何实现Go 超时控制
2021/03/30 Python
mysql5.6主从搭建以及不同步问题详解
2021/12/04 MySQL