记一次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对html代码进行escape编码的方法
May 04 Python
将Python代码打包为jar软件的简单方法
Aug 04 Python
浅谈python中copy和deepcopy中的区别
Oct 23 Python
浅析PHP与Python进行数据交互
May 15 Python
centos6.8安装python3.7无法import _ssl的解决方法
Sep 17 Python
python之pexpect实现自动交互的例子
Jul 25 Python
python实现KNN分类算法
Oct 16 Python
Python新手如何进行闭包时绑定变量操作
May 29 Python
使用Tensorflow-GPU禁用GPU设置(CPU与GPU速度对比)
Jun 30 Python
anaconda3安装及jupyter环境配置全教程
Aug 24 Python
python 发送邮件的四种方法汇总
Dec 02 Python
Jupyter Notebook 安装配置与使用详解
Jan 06 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 php_openssl.dll的作用
2013/07/01 PHP
php实现的单一入口应用程序实例分析
2015/09/23 PHP
CentOS下PHP7的编译安装及MySQL的支持和一些常见问题的解决办法
2015/12/17 PHP
php使用Jpgraph创建柱状图展示年度收支表效果示例
2017/02/15 PHP
php模式设计之观察者模式应用实例分析
2019/09/25 PHP
从零开始学习jQuery (三) 管理jQuery包装集
2011/02/23 Javascript
基于JQUERY的多级联动代码
2012/01/24 Javascript
通过Javascript将数据导出到外部Excel文档的函数代码
2012/06/15 Javascript
js创建对象的方式总结
2015/01/10 Javascript
js实现感应鼠标图片透明度变化的方法
2015/02/20 Javascript
基于JS设计12306登录页面
2016/12/28 Javascript
jQuery实现字符串全部替换的方法【推荐】
2017/03/09 Javascript
vue、react等单页面项目应该这样子部署到服务器
2018/01/03 Javascript
js中DOM事件绑定分析
2018/03/18 Javascript
vue-router中scrollBehavior的巧妙用法
2018/07/09 Javascript
基于element-ui的rules中正则表达式
2018/09/04 Javascript
Nodejs中获取当前函数被调用的行数及文件名详解
2018/12/12 NodeJs
详解nodejs 开发企业微信第三方应用入门教程
2019/03/12 NodeJs
nodejs使用node-xlsx生成excel的方法示例
2019/08/22 NodeJs
Vue.js仿Select下拉框效果
2020/02/18 Javascript
[01:11:21]DOTA2-DPC中国联赛 正赛 Phoenix vs CDEC BO3 第三场 3月7日
2021/03/11 DOTA
Python的Django框架中settings文件的部署建议
2015/05/30 Python
Python增量循环删除MySQL表数据的方法
2016/09/23 Python
pytorch 指定gpu训练与多gpu并行训练示例
2019/12/31 Python
解决Python3.8运行tornado项目报NotImplementedError错误
2020/09/02 Python
Python self用法详解
2020/11/28 Python
全球度假村:Club Med
2017/11/27 全球购物
英国最大的自有市场,比亚马逊便宜:Flubit
2019/03/19 全球购物
德国净水壶和滤芯品牌:波尔德PearlCo(家用净水器)
2020/04/29 全球购物
优秀的茶餐厅创业计划书
2014/01/03 职场文书
介绍信格式
2015/01/30 职场文书
小学生学习保证书
2015/02/26 职场文书
2015年市场部工作总结
2015/04/30 职场文书
班主任工作总结范文
2015/08/13 职场文书
公司年会晚会开幕词
2019/04/02 职场文书
导游词之天津盘山
2019/11/01 职场文书