让lua的gc替我们回收c的堆变量
更新日期:
因为lua和c的内存回收管理方式不同,所以使用c+lua的结构做开发时我们可以自由选择c创建出的供lua使用的对象(我们这里说的对象都是c中分配的堆变量)到底该由lua还是c来释放。我参与的前一个项目因为历史遗留问题选择了让c来释放对象的方案,也就是写两个lua_CFunction来封装c中一个对象的创建和销毁给lua用,做法如下:
|
|
|
|
这样做很符合c的使用习惯,但是却不符合lua的使用习惯,当我们在lua中使用的时候很可能忘记主动调用销毁函数去销毁对象,从而造成内存泄漏。这并不是危言怂听,我们项目曾经就因为这个原因出现过内存泄漏。所以我后面下决心修改掉了这些东西,选择让lua的gc帮我们自动释放c创建出的对象,这样我们在使用lua的时候就能够完全按照lua的习惯去开发了,大家也不必再去担心使用lua时忘记释放c对象而导致内存泄漏。
让lua的gc替我们回收c创建出的对象其实很简单,基本原理就是为c对象所绑定的userdata分配一个存在自定义__gc方法的metatable,这个__gc方法的作用就是为我们回收c对象。下面是个简单的例子:
|
|
|
|
整个代码只是增加了一些对元表和__gc元方法的处理,但却能让我们在lua中更简单的进行开发,不用再顾忌创建c对象会带来内存泄露的问题。为什么这样处理后packet对象会被lua的gc自动回收呢?原理主要是通过gc回收对象时触发__gc元方法实现的。首先,我们为绑定c对象所创建的userdata会被lua的gc所回收,而在gc进行回收时会触发元表中我们自定义的__gc元方法,我们自定义的这个__gc方法的作用就是释放掉userdata所绑定的c对象,因此我们就达到了让lua的gc替我们销毁对象的作用了。
这两种方法并不存在谁优谁略,只是看你想要什么样的效果。比如我要的就是开发更便利,消除忘记主动销毁对象带来的内存泄漏,所以我选择让lua的gc替我回收对象。而如果你的要求不同,比如你有个对象池,希望能够在不用对象时及时的放入对象池,那你最好还是自己手动调用回收函数。上面的代码是随手写的,没有测试过,不过思路就是这样的,大家只要明白思路就可以了,至于选择哪种方案,那就要你自己决定了。