记一次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判断windows隐藏文件的方法
Mar 21 Python
python使用正则表达式匹配字符串开头并打印示例
Jan 11 Python
Python 获取当前所在目录的方法详解
Aug 02 Python
Python实现读写sqlite3数据库并将统计数据写入Excel的方法示例
Aug 07 Python
python判断字符串是否是json格式方法分享
Nov 07 Python
Django实现简单分页功能的方法详解
Dec 05 Python
python适合人工智能的理由和优势
Jun 28 Python
python SocketServer源码深入解读
Sep 17 Python
python使用Thread的setDaemon启动后台线程教程
Apr 25 Python
Python经纬度坐标转换为距离及角度的实现
Nov 01 Python
用Python爬虫破解滑动验证码的案例解析
May 06 Python
Python OpenGL基本配置方式
May 20 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基础知识:类与对象(4) 范围解析操作符(::)
2006/12/13 PHP
php对二维数组进行排序的简单实例
2013/12/19 PHP
php获取数组元素中头一个数组元素值的实现方法
2014/12/20 PHP
使用PHP处理数据库数据如何将数据返回客户端并显示当前状态
2016/02/16 PHP
Yii2 队列 shmilyzxt/yii2-queue 简单概述
2017/08/02 PHP
kindeditor 加入七牛云上传的实例讲解
2017/11/12 PHP
php自定义排序uasort函数示例【二维数组按指定键值排序】
2019/06/19 PHP
自动更新作用
2006/10/08 Javascript
JQuery EasyUI 数字格式化处理示例
2014/05/05 Javascript
JavaScript 动态加载脚本和样式的方法
2015/04/13 Javascript
jquery中键盘事件小结
2016/02/24 Javascript
jQuery处理XML文件的几种方法
2016/06/14 Javascript
NodeJS与HTML5相结合实现拖拽多个文件上传到服务器的实现方法
2016/07/26 NodeJs
js捕捉键盘事件和按键键值的方法
2016/10/10 Javascript
总结js中的一些兼容性易错的问题
2017/12/18 Javascript
Vue中的methods、watch、computed的区别
2018/11/26 Javascript
vue和better-scroll实现列表左右联动效果详解
2019/04/29 Javascript
Nuxt页面级缓存的实现
2020/03/09 Javascript
Vue 中如何将函数作为 props 传递给组件的实现代码
2020/05/12 Javascript
JavaScript中作用域链的概念及用途讲解
2020/08/06 Javascript
[57:38]2018DOTA2亚洲邀请赛3月30日 小组赛A组 OpTic VS OG
2018/03/31 DOTA
解决python selenium3启动不了firefox的问题
2018/10/13 Python
Python tkinter和exe打包的方法
2020/02/05 Python
公认8个效率最高的爬虫框架
2020/07/28 Python
Python爬虫之爬取淘女郎照片示例详解
2020/07/28 Python
深入浅析CSS3中的Flex布局整理
2020/04/27 HTML / CSS
阿迪达斯意大利在线商店:adidas意大利
2016/09/19 全球购物
方正Java笔试题
2014/07/03 面试题
大学生物业管理求职信
2013/10/24 职场文书
高一新生军训方案
2014/05/12 职场文书
机电一体化专业毕业生自荐信
2014/06/19 职场文书
2014年副班长工作总结
2014/12/10 职场文书
师德承诺书2015
2015/04/28 职场文书
Python控制台输出俄罗斯方块的方法实例
2021/04/17 Python
详解MySQL数据库千万级数据查询和存储
2021/05/18 MySQL
vue-treeselect的基本用法以及解决点击无法出现拉下菜单
2022/04/30 Vue.js