« 登陆认证系统 | 返回首页 | Luacc »

一个 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

2012年。。。。。。。,貌似现在2020了,有人用这个技术在Unity发扬光大了,但始终 lua一直是在边缘地带啊,云风大哥是唯一灯塔,膜拜一下
云风大哥,我有个问题,如果有人在lua里实现了一个list,而且长度还很长,snapshot的时候栈会不会爆掉?我看代码似乎是会,不过我也不太确定。
有用, 大量数据的时候进行多次快照对比, 重复出现次数多的引用是内存泄漏的可能性越大。话说大佬的代码是不是被集成仅luvit库了, 可以直接require('snapshot')进行使用
我把这个工具集成到cocos2d-x里,只要一调用快照这个函数就直接崩溃了,用的cocos2d-x 3.6引擎,不知道有没有做过测试
好样的!分分钟把内存泄露找出来了^o^
这个工具不会用啊 编译成了dll
to iceman: mruby 现在仍在发展中,而且mruby 现在还没有 fiber 或 coroutine,或许后期会加入对fiber或coroutine的支持,但是现在来看,距离lua还是有不少差距,而且 mruby的生态圈也很年轻,还没出现类似 mruby-jit这样的项目。
mruby看起来是不错的lua备选方案
几个星期我们也有lua内存泄露,我们的做法是用lua写个程序从_G开始遍历整个lua节点,包括table,metatable,和函数的upvalue的引用的统计个数,查找那些top 100计数的引用,很容易定位,每次出现疑似内存泄露的时候做一下这个事情,就可以定位了,不用做snapshot。
搬个板凳,哈哈。法语翻译http://www.gvlocalization.com.cn/detail/fanyi-French.html
云风大哥,以后有没有考虑过用go开发游戏后端呢?
游戏逻辑还需要关注内存泄漏,个人感觉lua真心已经不适合游戏开发了。
没有使用lua,不过思想有益,学习了
也是第一次这么早看到,板凳
第一次坐沙发~

Post a comment

非这个主题相关的留言请到:留言本