聊一个有意思的话题, 一个让我如痴如醉的内存泄露问题 (memory leak). . .

熟悉我的人,知道我是从devops再到纯开发的岗位。虽然我写过很多的所谓运维平台,什么自动装机,什么集群管理,什么DBA管理,什么监控系统等等… 虽然前后端都是我自己搞,但我很并不喜欢写前端的东西,当然我写的那些的前端跟真正的前端没法比,只能说硬着头皮拿着angularjs来撸, 写的很一般 。  后来到了Admaster之后,发现写纯后端是如此的美妙,尤其是用python这种渣渣语言来实现高性能的服务,可以说坚定了我往后端底层深入的决心.   

貌似只要做过运维开发及运维的朋友,天然的喜欢高性能的东西,我也不例外。  我对高性能的软件架构很是感兴趣,为此我主动去熟悉各种的网络模型的优缺点,看一些用epoll实现的网络框架案例,把tornado和gevent的核心调度代码来回翻弄。 

还是具体说说我是怎么写出内存泄露的代码…  场景不复杂,就是实现的时候写了不少令人可气的业务逻辑.  

该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新.    http://xiaorui.cc/?p=3315

根据业务,我简单list一下流程:

A .  根据平台动态的加载业务代码,然后从redis取任务id

B .  从mysql里面取出id的相关所有数据, 这不是一条SQL就可以搞定的,需要各种的组合套取数据。 

C .  调用各种nlp,segment,scroe等等api

D .  加载AC自动机匹配关键词,自定义的摘要计算,黑白名单过滤,计数统计等等

E .  把数据扔到Elasticsearch里面.
F .   xxx
G .  xxxx

H.   xxxx

屁逻辑太多…  省略….

A,B,C,xxx函数逻辑是相关联的, 对于python的各种内存回收,我以前自信是理解的,但这次的内存泄露的BUG出来以后,让我很是醉心.  .  . 

这次遇到一个很严重的内存泄露问题,让我发现服务端开发并不是你理论知识好就可以搞定的。 学习是需要一个循环渐进的一个过程,每天睡觉前翻看APUE,不仅容易早睡,而且可以学点底层的东西。

这次开发项目服务名姑且叫service_migrate吧,这代码本身是有些复杂,在测试环境中是可以通过的,但在线上居然出现严重的内存泄露,这真是让人难以接受,120G的内存,被我给吃的9/10了…  因为服务有别的服务在跑,内核在不得已的情况下,把我的Worker进程给OOM干掉了.

要解决python的内存泄露,要理解python是怎么判定内存回收,  python是靠引用计数为主,分代回收为次的方案进行垃圾回收的。  现在一说python写的服务端,貌似大多是web服务的场景。 这个情况只要不用那种装13的黑魔法,很难出现大量的内存泄露问题,另外涉及到大量数据的运算不会在web层同步的返回,这也避免了内存泄露. 

python的哪里操作是会引用减少 ? 当一个对象引用数为0时,就会被回收.

- 函数运行结束,所有局部变量都被销毁,对象的引用计数也就随之减少。例如 foo(x) 运行结束,x 被销毁.
- 当变量被赋值给另一个对象时,原对象的引用计数也会减少.
- 使用 del 删除一个变量也会导致对象引用减少。例如 del x.
- 对象从集合对象中移除。例如 lst.remove(x).
- 包含对象的集合对象被销毁。例如 del lst.
其实很多时候,大家能见到的内存泄露代码都是可见的, 很容易看出来, 比如循环引用,这个还是比较好识别的!  另外是第三方的C模块写的不严谨,也会造成内存泄露。 

为了查找内存泄露的问题,我首先使用火焰图查看问题,结果没看出所以然。后来使用python解决内存泄露的经典工具memory_profiler, objgraph ,结果也是突然.   另外objgraph 还是有力度的,在我们已经知道内存泄露的点之后,再使用objgraph反推回去是可以验证的BUG的。 


这个应用服务管理着所有对外的实时数据,有点心急了,所有无法细心的琢磨这个工具的使用方法。 话说objgraph的文档是在粗糙,吐出来的内存数据也不好懂。还是那句话着急解决问题,没法静下心研究这工具。 通过stackoverload看到了一些回复,大多数是老生常谈的python引用计数的回收原理。 这原理做py的都懂,但问题代码中是在没有明显的问题代码。   

初步针对Python 内存泄露的解决方法:

     避免worker进程都被oom后,没有可用的worker,我又了一套这对worker的调度模块。    但这不是根本问题,有人说是ac自动机的问题,我跟同事用pyobject c代码又搞一轮子,还是出现,用了不少的内存泄露探测也没明显看出什么,只能是解耦拆解模块,来大量的数据测试。  最后才确定了问题。

 避免python worker进程都被oom后,没有可用的worker。  我这边把所有的平台都加入了Master Worker调度管理 ( 以前开源过这样的python进程调度管理工具,有兴趣的朋友到 https://github.com/rfyiamcool/ProcessHandler )。 这样master一直不断的把内存占用严重的进程干掉,并重新启动一个新进程,而针对每个worker做了max_requests限制,也就是说worker自己也会退出来。
虽然曲线救国绕过了OOM的坑,但这不是根本问题.
接着开始回溯这泄露问题,开始怀疑是ac自动机的问题,那我就直接用python搞一轮子,还是出现内存泄露,又怀疑Mysqldb的问题,好 ! 我把python Mysqldb的代码简单过了一遍.  话说myqsldb的代码写的那么复杂,根本没法懂其细节。
后来又用了不少的内存泄露工具也没明显看出什么,只能是一步步的解耦,拆解逻辑,拆解模块,然后通过大量的数据测试来确定问题。
说了这么多废话也该对python内存泄露问题做个总结了断了:

那么,如果要我单纯的说 怎么 解决内存泄露问题?

 一步步的拆解逻辑测试… 

话说对于后端的服务,不要只顾着功能逻辑测试,一定要兼顾着稳定性及cpu、内存的占比测试。 

下面是当时出现内存泄露的截图.  

我们可以发现出现了两种现象,CPU跑的满,内存占用很厉害。 解决了memory leak后,情况一切好转。 


很醒目的 Out of memory: KILL process pid…

/var/log/messages

推荐大家多研究下 objgraph模块,  可以打印对象的信息及引用,还可以打印出函数调用关系,像这样:



END。



对Python及运维开发感兴趣的朋友可以加QQ群 : 478476595 !!!
{ 2000人qq大群内有各厂大牛,常组织线上分享及沙龙,对高性能及分布式场景感兴趣同学欢迎加入该QQ群 }

另外如果大家觉得文章对你有些作用!   帮忙点击广告. 一来能刺激我写博客的欲望,二来好维护云主机的费用.
如果想赏钱,可以用微信扫描下面的二维码. 另外再次标注博客原地址  xiaorui.cc  ……   感谢!
暂无相关产品

3则回应给“一个让我如痴如醉的python内存泄露问题”

  1. 无感说道:

    说了这么多,我并没有收获

  2. 厉害说道:

    这个过程太痛苦了

发表评论