记一次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 相关文章推荐
使用django-suit为django 1.7 admin后台添加模板
Nov 18 Python
Python使用pylab库实现画线功能的方法详解
Jun 08 Python
利用python库在局域网内传输文件的方法
Jun 04 Python
Selenium鼠标与键盘事件常用操作方法示例
Aug 13 Python
华为校园招聘上机笔试题 扑克牌大小(python)
Apr 22 Python
对Python获取屏幕截图的4种方法详解
Aug 27 Python
Python实现TCP通信的示例代码
Sep 09 Python
Python 限定函数参数的类型及默认值方式
Dec 24 Python
Python @property及getter setter原理详解
Mar 31 Python
基于python爬取有道翻译过程图解
Mar 31 Python
解决python中0x80072ee2错误的方法
Jul 19 Python
Python 操作pdf pdfplumber读取PDF写入Exce
Aug 14 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
php4的彩蛋
2006/10/09 PHP
PHP 获取MySQL数据库里所有表的实现代码
2011/07/13 PHP
解析php如何将日志写进syslog
2013/06/28 PHP
php实现文件预览功能
2017/05/23 PHP
extjs表格文本启用选择复制功能具体实现
2013/10/11 Javascript
验证码在IE中不刷新而谷歌等浏览器正常的解决方案
2014/03/18 Javascript
JS中FRAME的操作问题实例分析
2014/10/21 Javascript
举例讲解Node.js中的Writable对象
2015/07/29 Javascript
ES6中如何使用Set和WeakSet
2016/03/10 Javascript
浅谈JavaScript中小数和大整数的精度丢失
2016/05/31 Javascript
Vue2学习笔记之请求数据交互vue-resource
2017/02/23 Javascript
Javascript 实现匿名递归的实例代码
2017/05/25 Javascript
JS实现图片预览的两种方式
2017/06/27 Javascript
通过jquery的ajax请求本地的json文件方法
2018/08/08 jQuery
python概率计算器实例分析
2015/03/25 Python
PyQt5每天必学之像素图控件QPixmap
2018/04/19 Python
python环境路径配置以及命令行运行脚本
2019/04/02 Python
详解python多线程之间的同步(一)
2019/04/03 Python
如何使用Python 打印各种三角形
2019/06/28 Python
Python Selenium 之数据驱动测试的实现
2019/08/01 Python
python pandas.DataFrame.loc函数使用详解
2020/03/26 Python
在python下实现word2vec词向量训练与加载实例
2020/06/09 Python
Django Admin 上传文件到七牛云的示例代码
2020/06/20 Python
Microsoft新加坡官方网站:购买微软最新软件和技术产品
2016/10/28 全球购物
电子信息工程专业推荐信
2014/02/14 职场文书
领导干部作风建设工作总结
2014/10/23 职场文书
师范生见习报告范文
2014/11/03 职场文书
2014年大学生工作总结
2014/11/20 职场文书
2014年企业党建工作总结
2014/12/18 职场文书
车间统计员岗位职责
2015/04/14 职场文书
2015年学校总务工作总结
2015/07/20 职场文书
2016年师德先进个人事迹材料
2016/02/29 职场文书
标准版个人借条怎么写?以及什么是借条?
2019/08/28 职场文书
最新农村养殖致富:资金投入较低的创业项目有哪些?
2019/09/26 职场文书
vue中利用mqtt服务端实现即时通讯的步骤记录
2021/07/01 Vue.js
企业版Windows 11有哪些新功能? Win11适用于企业的功能介绍
2021/11/21 数码科技