最近工作中慢慢开始用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非常方面的实时查看我们程序内存的占用情况,我们先将服务端启动,查看一下我们此时的内存情况可以看到占用的

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

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

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

二、解决内存泄漏的过程

像上面的例子是一个非常简单的程序,不复杂我们也并没有做上面复杂的操作就是一个简单的接受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行代码导致内存增长的最多,当然如果是我们复杂的项目也可以通过类似的方法,这样就可以非常快捷的找到我们代码中哪些地方会造成内存溢出,便于排查问题,我们点进去看看这行代码:

我们找到最终行,这个时候我们大致就可以看出哪里的问题了,我们接着看  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 内存泄漏解决过程的更多相关文章

  1. python 内存泄漏调试

    Python应用程序内存泄漏的调试 Quake Lee quakelee@geekcn.org 新浪网技术(中国)有限公司 Sina Research & Development Python ...

  2. 填坑总结:python内存泄漏排查小技巧

    摘要:最近服务遇到了内存泄漏问题,运维同学紧急呼叫解决,于是在解决问题之余也系统记录了下内存泄漏问题的常见解决思路. 本文分享自华为云社区<python内存泄漏排查小技巧>,作者:luti ...

  3. iOS常见内存泄漏解决

    iOS常见内存泄漏解决     1 OC和CF转化出现的内存警告 CFStringRef cfString = CFURLCreateStringByAddingPercentEscapes(kCFA ...

  4. 【原创】python内存泄漏以及python flask框架莫名coredump

    1.python内存泄漏 今天在看服务器上的进程时,用top查的时候,发现一个一直跑的脚本程序内存竟然达到了1.6G,这个脚本我有印象,一开始仅占用20M左右,显然是内存泄漏了. 用gc和objgra ...

  5. 一次 Java 内存泄漏排查过程,涨姿势

    人人都会犯错,但一些错误是如此的荒谬,我想不通怎么会有人犯这种错误.更没想到的是,这种事竟发生在了我们身上.当然,这种东西只有事后才能发现真相.接下来,我将讲述一系列最近在我们一个应用上犯过的这种错误 ...

  6. JAVA内存泄漏解决办法

    JVM调优工具 Jconsole,jProfile,VisualVM Jconsole : jdk自带,功能简单,但是可以在系统有一定负荷的情况下使用.对垃圾回收算法有很详细的跟踪.详细说明参考这里 ...

  7. 记一次使用windbg排查内存泄漏的过程

    一.背景 近期有一个项目在运行当中出现一些问题,程序顺利启动,但是观察一阵子后发现内存使用总量在很缓慢地升高, 虽然偶尔还会往下降一些,但是总体还是不断上升:内存运行6个小时候从33M上升到80M: ...

  8. 『神坑』DotNetty 内存泄漏 解决办法

    背景 近来在用 DotNetty 实现一个文件上传下载的同步服务. 其中:客户端下载服务端的文件,客户端多次请求,从服务端将文件分片下载下来,追加到本地磁盘. —— 非常简单的代码,都写了几十次了,驾 ...

  9. python内存泄漏

    记录: 一个脚本在连续运行后,使用内存越来越大,在循环后手动添加gc.collect()没有作用. 尝试方法: 去除所有函数中当作参数传入的全局变量 使用全局redis对象,不再当作参数传入 循环末尾 ...

随机推荐

  1. JVM之基本结构

    1. Java虚拟机的架构 1.1 Java的NIO库允许Java程序使用直接内存,访问直接内存的速度优于Java堆.出于性能的考虑,读写频繁的场合会考虑使用直接内存. 1.2 本地方法栈和Java栈 ...

  2. Cisco 12系列 AP 初始化配置-1-安装IOS

    12系列AP虽然已经淘汰了,但是像我们这种没钱的公司用了10年却还是在用,好在它还有学习的价值,还是可以从12系列AP看出一些思科部署无线的思路吧. 首先吐槽一下国内常说的胖.瘦AP的这种说法,因为用 ...

  3. spring 启动异常Failed to read candidate component class

    Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: ...

  4. IDEA中的常用设置

    ps:对于开发工具,不同的开发人员有不同的设置喜好,这里介绍的是我个人的配置,不喜勿喷. Appearance:个人喜欢全黑主题,雅黑字体 背景图片, 效果如下,编写代码的时候有个自己喜欢的背景图片, ...

  5. 2006 ACM 求奇数的和

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=2006 注意 sum=1,写在while 不然每次结果会累积 #include <stdio.h> ...

  6. Android应用程序结构

    综述:Android应用程序包含哪些部分? assets 可以出发一些随程序打包的文件,应用程序运行时可以动态读取到这些文件的内容. 如果使用到webview加载本地网页的功能,所有网页相关的文件都存 ...

  7. Meanshift算法学习

    ref:参考自:这里(目标跟踪) Meanshift图像分割:这里 最近看到FT算法使用meanshift算法进行显著图的分割,于是就来学习他的姿势 对于集合中的每一个元素,对它执行下面的操作:把该元 ...

  8. C++学习笔记52:查找

    //函数查找 template <class T> int seqSearch(const T list[], int n, const T &key) { ; i < n; ...

  9. C++学习笔记51:排序

    //直接插入排序函数模板 template <class T> void insertionSort(T a[], int n) { int i, ; T temp; ; i < n ...

  10. sklearn神经网络分类

    sklearn神经网络分类 神经网络学习能力强大,在数据量足够,隐藏层足够多的情况下,理论上可以拟合出任何方程. 理论部分 sklearn提供的神经网络算法有三个: neural_network.Be ...