记一次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之通过Python连接数据库
Oct 28 Python
python实现12306火车票查询器
Apr 20 Python
Python的爬虫框架scrapy用21行代码写一个爬虫
Apr 24 Python
利用Python将文本中的中英文分离方法
Oct 31 Python
python实现简易动态时钟
Nov 19 Python
python爬虫URL重试机制的实现方法(python2.7以及python3.5)
Dec 18 Python
总结python中pass的作用
Feb 27 Python
Python将文字转成语音并读出来的实例详解
Jul 15 Python
Python人工智能之路 之PyAudio 实现录音 自动化交互实现问答
Aug 13 Python
python 3.7.4 安装 opencv的教程
Oct 10 Python
python多线程使用方法实例详解
Dec 30 Python
Python Sphinx使用实例及问题解决
Jan 17 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 清空varnish 缓存的详解(包括指定站点下的)
2013/06/20 PHP
php实现在线考试系统【附源码】
2018/09/18 PHP
将字符串转换成gb2312或者utf-8编码的参数(js版)
2013/04/10 Javascript
js中单引号与双引号冲突问题解决方法
2013/10/04 Javascript
鼠标事件的screenY,pageY,clientY,layerY,offsetY属性详解
2015/03/12 Javascript
JS截取与分割字符串常用技巧总结
2015/11/10 Javascript
Javascript设计模式之观察者模式(推荐)
2016/03/29 Javascript
JavaScript中 ES6 generator数据类型详解
2016/08/11 Javascript
javascript解析ajax返回的xml和json格式数据实例详解
2017/01/05 Javascript
微信小程序技巧之show内容展示,上传文件编码问题
2017/01/23 Javascript
nodejs实现截取上传视频中一帧作为预览图片
2017/12/10 NodeJs
解决vue2中使用axios http请求出现的问题
2018/03/05 Javascript
Vue render渲染时间戳转时间,时间转时间戳及渲染进度条效果
2018/07/27 Javascript
vue解决一个方法同时发送多个请求的问题
2018/09/25 Javascript
利用React Router4实现的服务端直出渲染(SSR)
2019/01/07 Javascript
解决JQuery的ajax函数执行失败alert函数弹框一闪而过问题
2019/04/10 jQuery
Python的Tornado框架的异步任务与AsyncHTTPClient
2016/06/27 Python
Python处理文本文件中控制字符的方法
2017/02/07 Python
遗传算法之Python实现代码
2017/10/10 Python
python enumerate函数的使用方法总结
2017/11/15 Python
详解用python自制微信机器人,定时发送天气预报
2019/03/25 Python
python实点云分割k-means(sklearn)详解
2020/05/28 Python
pyqt5 textEdit、lineEdit操作的示例代码
2020/08/12 Python
CSS3 实现侧边栏展开收起动画
2014/12/22 HTML / CSS
仿CSDN Blog返回页面顶部功能实现原理及代码
2013/06/30 HTML / CSS
巴西化妆品商店:Lojas Rede
2019/07/26 全球购物
请写出 float x 与"零值"比较的 if 语句
2016/01/04 面试题
六一儿童节活动策划方案
2014/01/27 职场文书
三查三看党性分析材料
2014/02/18 职场文书
酒店管理专业毕业生求职自荐信
2014/04/28 职场文书
班主任工作经验交流材料
2014/05/13 职场文书
邻里守望志愿服务活动方案
2014/08/15 职场文书
2015年银行个人工作总结
2015/05/14 职场文书
七一活动主持词
2015/06/29 职场文书
《狼牙山五壮士》教学反思
2016/02/17 职场文书
好段摘抄大全(48句)
2019/08/08 职场文书