一个 Lua 内存泄露检查工具
昨天我们发现每日构建的服务器突然在一个晚上内存暴增了 8 G ,显然是发生了内存泄露。
之前,我们在 skynet 里留下了许多调试协议,使我们很快的确定了发生泄露的服务:在一张地图的 lua State 中。可以确定是地图的 lua 实现中,有些 lua 对象在不断的生成。生成速度不快,但确实没有人解开引用,导致内存持续增长。
曾经有很多人做过 Lua 的内存分析工具,但是我懒的去搜了,花了半天时间自己写了一个。(已经开源在 github 上)
原理是这样的:
这个叫作 snapshot 的库,只提供一个函数,它可以对当前的 Lua State 做一个完整的快照。但由于我并不像做 Lua State 的序列化工作(虽然做法很像),为了减少分析数据,只记录了复杂对象的引用关系。
即,记录下所有 table thread userdata function 间的引用。
我们可以在不同时间,对 Lua State 拍两个快照,相比较后,就很容易知道新增加的内存处于何处了。
一开始我想用 lua 来编写这个工具,其实也的确做的到。但如果有 Lua 写就得相当小心,因为 Lua 代码的运行过程本身会影响 State ,即,你想对它观察,就可能改变它。
用 C 直接调用 lua API 来遍历 lua State ,影响要小的多。
最终得到的对象引用关系数据其实很丰富,但因为我希望结果对 State 的影响小一些,就把一些信息合并成字符串储存到结果表内了。 snapshot 的返回结果只是一张简单的 table ,每个 Lua 对象,都以指针(lightuserdata) 为 key 储存在表中;对应的 value 是一个 string 足够详细的描述了这个对象的引用关系。
比如例子中的 dump.lua 这段程序,运行后就会得到这样的结果:
userdata: 0052E760 table 00521810 : tmp : dump.lua:7 userdata: 0052EB98 table 00521810 : S1 : dump.lua:7
这表示,两次 snapshot 间,增加了两个 table 。它们是被运行在源代码 dump.lua:7 处的函数中 tmp 和 S1 两个变量引用住了。
snapshot 生成的报告不太利于人阅读,但你可以写代码做进一步分析。比如可以逐级建立 table 的引用关系,用更人性的方式展现出来。由于只是一个临时工具,它也已经快速的帮我们定位了昨天的内存泄露问题所在,暂时我就不花精力去完善它了。
Comments
Posted by: avi9111 | (15) December 18, 2019 06:02 PM
Posted by: 王铁猫 | (14) July 9, 2018 12:13 AM
Posted by: suchp | (13) March 9, 2018 05:03 PM
Posted by: pClass | (12) December 10, 2015 04:32 PM
Posted by: 胖纸 | (11) November 25, 2015 04:31 PM
Posted by: rocky | (10) October 17, 2013 05:24 PM
Posted by: skandhas | (9) December 17, 2012 10:41 AM
Posted by: icemark | (8) December 14, 2012 03:11 PM
Posted by: Siney | (7) December 13, 2012 05:55 PM
Posted by: jiayutong01 | (6) December 13, 2012 03:14 PM
Posted by: xishvai | (5) December 13, 2012 11:13 AM
Posted by: www | (4) December 12, 2012 11:45 PM
Posted by: r | (3) December 12, 2012 10:40 PM
Posted by: 点击这里 | (2) December 12, 2012 07:39 PM
Posted by: xishvai | (1) December 12, 2012 03:03 PM