上一篇文章找同事review了一下,收到的反馈是铺垫太长了,我尽可能直入正题,哈哈html
最近dbd压测时发现内存泄漏,其实这个问题去年已经暴露了,参见这篇博客【压测周】。当时排查不够仔细,在此反省下。关于dbd的内存问题,还有这篇博客讨论线程安全,以及这篇博客讨论临时变量的处理。当时还存了一个尾巴,由于用到关联数组进行脏数据管理,这部分数据是怎么释放的一直没搞明白。今天算是搞明白了,这个内存泄漏的bug也源于此。golang
基于引用计数的gc机制,应该是在引用计数为0的时候释放内存的,lpc也基本没有例外。具体详情能够参看free_svalue的实现,里面基本是引用计数减去1,而后检查是否引用计数为0,如果0就执行相关的Free操做。其中,有一半的篇幅都是讲字符串的释放的,剩下的则是object、buffer、array、mapping、class以及function的释放问题。因此,基本上,lpc不须要像golang同样,中止一切操做作mark and swap的gc操做。也不一样于Lua的标记清除,不须要分步执行,只要引用计数为0,立马释放掉。数组
为何是基本上呢?由于对于object类型,lpc并无对其当即释放,而是放到一个链表里面,待主循环里没有函数执行时,再行销毁。这样作,一方面是为了配合复杂的热更新机制,另外一方面,则是解决object循环引用的须要。对于对象的销毁,首先调用的是destruct_object, 这个函数最终的做用是为对象打上销毁标志。在这个函数里,针对master object和simul_efun object作了热更新的处理。而后从栈上移除object的全部引用,从object#n列表里移除这个object。最后打上销毁标记,并放入待销毁列表。安全
下一步,则调用destruct2, 首先将该object的全部变量释放。若是该对象有对自身引用,则由于这一步而引用计数减一,方便接下来释放该对象自己。接着调用free_object, 对象引用次数为0,已打上销毁标记,功成身退,成功析构。app
因为在dbd中,没有跑lpc脚本,全部的关联数组(mapping)都是在C层生成和使用的,所以引用计数须要手工维护,这就是万恶之源了。对于脚本层,新建的变量,引用计数都应该为1,退出变量做用域时,则引用计数减一。放入mapping时,引用次数相应加1。而在C层使用mapping时,全部变量的组织都是围绕mapping展开的,变量的生命周期就等同于mapping的生命周期。当序列化数据后,释放数据对应的mapping时,会对其中的全部变量引用计数-1,但依然计数不为0,所以没法释放。函数
解决的办法也至关简单,变量放入mapping前,先free_svalue,将引用计数变为0,则加入mapping后,引用计数为1,生命周期就同mapping同样了。线程