熟悉我的人,知道我是从devops再到纯开发的岗位。虽然我写过很多的所谓运维平台,什么自动装机,什么集群管理,什么DBA管理,什么监控系统等等… 虽然前后端都是我自己搞,但我很并不喜欢写前端的东西,当然我写的那些的前端跟真正的前端没法比,只能说硬着头皮拿着angularjs来撸, 写的很一般 。 后来到了Admaster之后,发现写纯后端是如此的美妙,尤其是用python这种渣渣语言来实现高性能的服务,可以说坚定了我往后端底层深入的决心.
貌似只要做过运维开发及运维的朋友,天然的喜欢高性能的东西,我也不例外。 我对高性能的软件架构很是感兴趣,为此我主动去熟悉各种的网络模型的优缺点,看一些用epoll实现的网络框架案例,把tornado和gevent的核心调度代码来回翻弄。
还是具体说说我是怎么写出内存泄露的代码… 场景不复杂,就是实现的时候写了不少令人可气的业务逻辑.
该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新. http://xiaorui.cc/?p=3315
根据业务,我简单list一下流程:
B . 从mysql里面取出id的相关所有数据, 这不是一条SQL就可以搞定的,需要各种的组合套取数据。
D . 加载AC自动机匹配关键词,自定义的摘要计算,黑白名单过滤,计数统计等等
H. xxxx
屁逻辑太多… 省略….
这次遇到一个很严重的内存泄露问题,让我发现服务端开发并不是你理论知识好就可以搞定的。 学习是需要一个循环渐进的一个过程,每天睡觉前翻看APUE,不仅容易早睡,而且可以学点底层的东西。
这次开发项目服务名姑且叫service_migrate吧,这代码本身是有些复杂,在测试环境中是可以通过的,但在线上居然出现严重的内存泄露,这真是让人难以接受,120G的内存,被我给吃的9/10了… 因为服务有别的服务在跑,内核在不得已的情况下,把我的Worker进程给OOM干掉了.
要解决python的内存泄露,要理解python是怎么判定内存回收, python是靠引用计数为主,分代回收为次的方案进行垃圾回收的。 现在一说python写的服务端,貌似大多是web服务的场景。 这个情况只要不用那种装13的黑魔法,很难出现大量的内存泄露问题,另外涉及到大量数据的运算不会在web层同步的返回,这也避免了内存泄露.
python的哪里操作是会引用减少 ? 当一个对象引用数为0时,就会被回收.
为了查找内存泄露的问题,我首先使用火焰图查看问题,结果没看出所以然。后来使用python解决内存泄露的经典工具memory_profiler, objgraph ,结果也是突然. 另外objgraph 还是有力度的,在我们已经知道内存泄露的点之后,再使用objgraph反推回去是可以验证的BUG的。
这个应用服务管理着所有对外的实时数据,有点心急了,所有无法细心的琢磨这个工具的使用方法。 话说objgraph的文档是在粗糙,吐出来的内存数据也不好懂。还是那句话着急解决问题,没法静下心研究这工具。 通过stackoverload看到了一些回复,大多数是老生常谈的python引用计数的回收原理。 这原理做py的都懂,但问题代码中是在没有明显的问题代码。
初步针对Python 内存泄露的解决方法:
避免worker进程都被oom后,没有可用的worker,我又了一套这对worker的调度模块。 但这不是根本问题,有人说是ac自动机的问题,我跟同事用pyobject c代码又搞一轮子,还是出现,用了不少的内存泄露探测也没明显看出什么,只能是解耦拆解模块,来大量的数据测试。 最后才确定了问题。
那么,如果要我单纯的说 怎么 解决内存泄露问题?
一步步的拆解逻辑测试…
下面是当时出现内存泄露的截图.
我们可以发现出现了两种现象,CPU跑的满,内存占用很厉害。 解决了memory leak后,情况一切好转。
很醒目的 Out of memory: KILL process pid…
#xiaorui.cc May 01 17:45:11 HK-Hbase kernel: [40996] 508 40996 2285 12 12 0 0 sh May 01 17:45:11 HK-Hbase kernel: Out of memory: Kill process 39752 (backtrack_migra) score 88 or sacrifice child May 01 17:45:11 HK-Hbase kernel: Killed process 39752, UID 508, (backtrack_migra) total-vm:11863168kB, anon-rss:11618328kB, file-rss:4kB May 01 17:46:01 HK-Hbase kernel: backtrack_migra invoked oom-killer: gfp_mask=0x280da, order=0, oom_adj=0, oom_score_adj=0 May 01 17:46:01 HK-Hbase kernel: backtrack_migra cpuset=/ mems_allowed=0-1 May 01 17:46:01 HK-Hbase kernel: Pid: 39759, comm: backtrack_migra Not tainted 2.6.32-504.16.2.el6.x86_64 #1 May 01 17:46:01 HK-Hbase kernel: Call Trace: May 01 17:46:01 HK-Hbase kernel: [<ffffffff810d41b1>] ? cpuset_print_task_mems_allowed+0x91/0xb0 May 01 17:46:01 HK-Hbase kernel: [<ffffffff81127410>] ? dump_header+0x90/0x1b0 May 01 17:46:01 HK-Hbase kernel: [<ffffffff8122ee4c>] ? security_real_capable_noaudit+0x3c/0x70 May 01 17:46:01 HK-Hbase kernel: [<ffffffff81127892>] ? oom_kill_process+0x82/0x2a0 May 01 17:46:01 HK-Hbase kernel: [<ffffffff811277d1>] ? select_bad_process+0xe1/0x120
/var/log/messages
#xiaorui.cc pr 01 17:46:01 HK-Hbase kernel: [ 5887] 0 5887 64063 23528 3 0 0 worker: extract May 01 17:46:01 HK-Hbase kernel: [ 6809] 508 6809 1295200 1217202 2 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [10512] 0 10512 60903 21412 1 0 0 worker: extract May 01 17:46:01 HK-Hbase kernel: [10620] 0 10620 61001 21463 5 0 0 worker: extract May 01 17:46:01 HK-Hbase kernel: [11669] 508 11669 268832 214311 17 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [13230] 508 13230 374508 303953 5 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [16926] 508 16926 0188551 0129408 12 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [19710] 0 01710 63847 23266 23 0 0 worker: extract May 01 17:46:01 HK-Hbase kernel: [20593] 508 20593 1287613 1233080 20 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [22162] 508 22162 347829 280500 23 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [24890] 508 24890 281240 210594 11 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [28477] 508 28477 1123502 1047891 16 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [30886] 0 30886 1072 22 7 0 0 sleep May 01 17:46:01 HK-Hbase kernel: [35346] 508 35346 265765 211342 3 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [36858] 508 36858 971333 916659 22 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [37631] 508 37631 282119 211463 1 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [38789] 508 38789 184960 130535 1 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [41008] 508 41008 57681 3256 21 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: [41521] 0 41521 1072 23 14 0 0 sleep May 01 17:46:01 HK-Hbase kernel: [44142] 0 44142 1072 24 4 0 0 sleep May 01 17:46:01 HK-Hbase kernel: [44143] 0 44143 1072 23 1 0 0 sleep May 01 17:46:01 HK-Hbase kernel: [44144] 0 44144 1072 23 6 0 0 sleep May 01 17:46:01 HK-Hbase kernel: [44149] 0 44149 1073 24 6 0 0 sleep May 01 17:46:01 HK-Hbase kernel: [44194] 508 44194 57681 3250 23 0 0 backtrack_migra May 01 17:46:01 HK-Hbase kernel: Out of memory: Kill process 39749 (backtrack_migra) score 77 or sacrifice child
推荐大家多研究下 objgraph模块, 可以打印对象的信息及引用,还可以打印出函数调用关系,像这样:
END。
说了这么多,我并没有收获
确实说了不少废话,虽然前期使用了不少检测工具. 因为内存增长快,所以用拆逻辑的方法很容易发现问题.
这个过程太痛苦了