处理由引用计数引起的泄漏
网游服务器的逻辑一般来说比较复杂,而且在很多情况下还使用了多线程,因此使用基于引用计数的智能指针能很大程度的减少内存泄漏和对象失效等问提.
但是基于引用计数的指针在很多情况下也会产生另一种情况的泄漏,例如:网游中有一个代表角色的类型character,角色的对象在网游中可以说是最常见的对象
之一了,与几乎所有的游戏逻辑都有关系,因此,一个角色对象可能会存在于很多容器,或被其它的对象所持有,为了保证角色对象的有效性,常常会使用基
于引用计数的智能指针来存放角色对象。问题由此产生了,例如当一个角色离开地图,我们就需要把所有指向这个角色的智能指针清0,稍有不甚,漏掉一个都
会导致资源无法释放,而且要找到具体还被哪些地方持有是相当麻烦的事情.
在我们项目中,处理这种情况的做法是,不使用智能指针来存放对象,而是采用了另外一种叫做ident的对象.对象被创建之后,只由一个容器持有其原始对象,
其它的所有外部引用持有的都是那个由原始对象产生的ident对像.当逻辑需要使用原始对象时,通过ident转换成原始对象,如果原始对象依旧有效则返回原始
对象,否则,返回NULL。首先,原始对象是基于引用计数的对象,其中有一个64位的identity成员,其高32位是一个全局计数器的值,低32位是一个时间戳.对象被创建的时候,
identity被正确的初始化,被销毁时将identity置0,这样,两个对象identity拥有相同值的概率是相当低的.
然后看下ident对象,只有一备份的identity和一个指向原始对象的指针,通过make_ident函数,可以通过一个原始对象的指针产生一个ident对象.然后,
可以通过cast_2_refbase将一个ident对象转换回原始指针,如果转换成功,原始对象的引用加1,防止对象正在使用的时候被其它线程释放掉.只要在
使用完毕后调用ref_decrease清理即可.
refbase.h
#ifndef _REFBASE_H #define _REFBASE_H #include <stdint.h> #include <stdlib.h> #include <time.h> #include <sys/time.h> #include "atomic.h" extern atomic_32_t global_counter; struct refbase { atomic_32_t refcount; atomic_64_t identity; atomic_32_t flag; void (*destroyer)(void*); }; void ref_init(struct refbase *r,void (*destroyer)(void*),int32_t initcount); static inline atomic_32_t ref_increase(struct refbase *r) { return ATOMIC_INCREASE(&r->refcount); } static inline atomic_32_t ref_decrease(struct refbase *r) { atomic_32_t count; if((count = ATOMIC_DECREASE(&r->refcount)) == 0){ r->identity = 0; _FENCE; int32_t c = 0; for(;;){ if(COMPARE_AND_SWAP(&r->flag,0,1)) break; if(c < 4000){ ++c; __asm__("pause"); }else{ struct timespec ts = { 0, 500000 }; nanosleep(&ts, NULL); } } r->destroyer(r); } return count; } typedef struct ident { uint64_t identity; struct refbase *ptr; }ident; static inline ident make_ident(struct refbase *ptr) { ident _ident = {ptr->identity,ptr}; return _ident; } static inline ident make_empty_ident() { ident _ident = {0,NULL}; return _ident; } static inline struct refbase *cast_2_refbase(ident _ident) { while(_ident.identity == _ident.ptr->identity) { if(COMPARE_AND_SWAP(&_ident.ptr->flag,0,1)) { struct refbase *ptr = NULL; if(_ident.identity == _ident.ptr->identity && ref_increase(_ident.ptr) > 0) ptr = _ident.ptr; _FENCE; _ident.ptr->flag = 0; return ptr; } } return NULL; } static inline int32_t is_vaild_ident(ident _ident) { if(!_ident.ptr || !_ident.identity) return 0; return 1; } #define TO_IDENT(OTHER_IDENT) (*(ident*)&OTHER_IDENT) #endif
refbase.c
#include "refbase.h" #include "SysTime.h" atomic_32_t global_counter = 0; void ref_init(struct refbase *r,void (*destroyer)(void*),int32_t initcount) { r->destroyer = destroyer; r->identity = ATOMIC_INCREASE(&global_counter); r->identity <<= 32; r->identity += GetSystemMs(); r->refcount = initcount; }
大致处理逻辑如下:
ident _ident; struct ref_base *atker = cast_2_refbase(_ident); if(atker) { //对象依然有效,执行某些逻辑 ...... ref_decrease(&atker);//不再使用了,减少计数 } else { //原对象已经失效 }