Lua GC垃圾回收优化方案
最近接手的一个游戏项目是重 Lua 的结构(网络模块在 C++,其余逻辑全在 Lua)。和许多用 Lua 的游戏项目一样,遇到了 Lua 的垃圾回收的性能问题,经常跑着跑着就会掉帧,因此花了一周的时间,给 Lua 虚拟机写了个模块,把 Lua 垃圾回收的速度提高了一个量级。
思路
这个思路其实在之前的一篇博客中也有提到,想要 垃圾回收快,无非就那么几种思路。
- 使用内存池
- 减少对象生成
- 垃圾回收提速
使用内存池
第一种思路,我觉得不合理,因为现代的内存分配器早就有内存池的设计了,手写一个内存池的收益并不大。
减少对象生成
第二种思路,是比较合理的。因为我在项目的代码中发现很多处地方有动态生成 Closure 的情况。
1 | function test() |
上面那个例子,每次调用到 test 函数的时候,都会动态根据 fn 的 函数原型,生成一个 Closure
可能有人会问,Proto 不是有一个 cache 指向 Closure 吗?按道理这里 没有 UpValue(即代表UpValue 完全相同),应该会复用啊,但是很可惜,执行完这个函数以后,因为没有对象指向 Closure 用完再不久的将来又会被回收。
因此,少写这种代码就可以减少对象的生成。
垃圾回收提速
第三种思路,我的想法是,让垃圾回收所要遍历的对象大幅减少,就可以为垃圾回收提速了,由于我们是重 Lua 的框架,因此我们的所有配置都存在于 Lua 的 table中,而这一部分肯定是不需要被回收的,但是每次垃圾回收的时候,又会不停的扫描递归遍历,不合理。同时代码中的很多全局函数,也是根本不需要被回收的,也会被扫描到,于是就想到一个想法,给这些对象打上标记,让他们不被遍历不被清理,就可以大幅度的提速了。
原理简单,但是做起来确实挺难受的,要注意要手动关闭 UpValue 将其保留下来。
目前已经开源,LuaJIT-5.3.6源码。