Redis源码分析(二十八)--- object创建和释放redisObject对象

来源:互联网 发布:经典动画片形象 知乎 编辑:程序博客网 时间:2024/05/12 04:29

          今天的学习效率比较高,把Rio分析完了,又顺便学习了其中的RedisObject的文件,只要讲的就是RedisObject的一些转换和创建。里面的大多数方法都是非常类似的。列出里面长长的API列表:

/* ------------  API --------------------- */robj *createObject(int type, void *ptr) /* 最初的创建robj对象方法,后面的创建方法与此类似 */robj *createStringObject(char *ptr, size_t len)robj *createStringObjectFromLongLong(long long value)robj *createStringObjectFromLongDouble(long double value)robj *dupStringObject(robj *o)robj *createListObject(void)robj *createZiplistObject(void)robj *createSetObject(void)robj *createIntsetObject(void)robj *createHashObject(void)robj *createZsetObject(void)robj *createZsetZiplistObject(void)void freeStringObject(robj *o) /* free Obj中的特定对象,这里free的是r->ptr */void freeListObject(robj *o)void freeSetObject(robj *o)void freeZsetObject(robj *o)void freeHashObject(robj *o) /* 释放hashObject有2种形式,1个是o-ptr的字典对象,还有1个回事压缩表o->ptr */void incrRefCount(robj *o) /* robj对象增减引用计数,递增robj中的refcount的值 */void decrRefCount(robj *o) /* 递减robj中的引用计数,引用到0后,释放对象 */void decrRefCountVoid(void *o)robj *resetRefCount(robj *obj)int checkType(redisClient *c, robj *o, int type) /* 检查robj的类型是否为给定的Type类型 */int isObjectRepresentableAsLongLong(robj *o, long long *llval)robj *tryObjectEncoding(robj *o) /* 编码一个robj中的额字符对象,主要是为了省空间 */robj *getDecodedObject(robj *o) /* 获取解码后的robj */int compareStringObjectsWithFlags(robj *a, robj *b, int flags)int compareStringObjects(robj *a, robj *b)int collateStringObjects(robj *a, robj *b)int equalStringObjects(robj *a, robj *b)size_t stringObjectLen(robj *o)int getDoubleFromObject(robj *o, double *target) /* 从robj中获取double数值 */int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg)int getLongDoubleFromObject(robj *o, long double *target)int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg)int getLongLongFromObject(robj *o, long long *target)int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg)int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg)char *strEncoding(int encoding)unsigned long estimateObjectIdleTime(robj *o)robj *objectCommandLookup(redisClient *c, robj *key) /* obj的查找命令, */robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply)void objectCommand(redisClient *c)
从前往后看,第一个创建obj:

/* 最初的创建robj对象方法 */robj *createObject(int type, void *ptr) {    robj *o = zmalloc(sizeof(*o));    o->type = type;    o->encoding = REDIS_ENCODING_RAW;    o->ptr = ptr;    o->refcount = 1;    /* Set the LRU to the current lruclock (minutes resolution). */    o->lru = server.lruclock;    return o;}
有创建就必然会有释放的free方法:

/* free Obj中的特定对象 */void freeStringObject(robj *o) {    if (o->encoding == REDIS_ENCODING_RAW) {        sdsfree(o->ptr);    }}
free方法有很多衍生的方法,看你要释放哪种类型的空间了,可以,set,dict,ziplist等等。有下面的一些类型:

 switch(o->type) {        case REDIS_STRING: freeStringObject(o); break;        case REDIS_LIST: freeListObject(o); break;        case REDIS_SET: freeSetObject(o); break;        case REDIS_ZSET: freeZsetObject(o); break;        case REDIS_HASH: freeHashObject(o); break;        default: redisPanic("Unknown object type"); break;        }
重点介绍里面的关于引用计数的相关方法,通过robj->refcount的数值进行控制的:

/* robj对象增减引用计数,递增robj中的refcount的值 */void incrRefCount(robj *o) {//递增robj中的refcount的值    o->refcount++;}
增加引用计数就一行代码,但是递减的话,我们猜也可以猜到,引用计数变为0的时候,说明无人使用了,就可以释放空间了;

/* 递减robj中的引用计数,引用到0后,释放对象 */void decrRefCount(robj *o) {//如果之前的引用计数已经<=0了,说明出现异常情况了    if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");    if (o->refcount == 1) {    //如果之前的引用计数为1,再递减一次,恰好内有被任何对象引用了,所以就可以释放对象了        switch(o->type) {        case REDIS_STRING: freeStringObject(o); break;        case REDIS_LIST: freeListObject(o); break;        case REDIS_SET: freeSetObject(o); break;        case REDIS_ZSET: freeZsetObject(o); break;        case REDIS_HASH: freeHashObject(o); break;        default: redisPanic("Unknown object type"); break;        }        zfree(o);    } else {    //其他对于>1的引用计数的情况,只需要按常规的递减引用计数即可        o->refcount--;    }}
标准的引用计数法控制内存的管理,(提醒一下,在JVM中的对象的生命周期管理用的是根搜索法,不是引用计数),还有一个在robj中的编码方法的实现也是定义在这个文件中:

/* Try to encode a string object in order to save space *//* 编码一个robj中的额字符对象,主要是为了省空间 */robj *tryObjectEncoding(robj *o) {    long value;    sds s = o->ptr;    size_t len;    if (o->encoding != REDIS_ENCODING_RAW)    //如果robj已经被编码了,则直接返回        return o; /* Already encoded */    /* It's not safe to encode shared objects: shared objects can be shared     * everywhere in the "object space" of Redis. Encoded objects can only     * appear as "values" (and not, for instance, as keys) */     /* 如果robj的引用计数超过1个人引用的时候,是不安全的去编码obj,因为对象是共享的 */     if (o->refcount > 1) return o;    /* Currently we try to encode only strings */    redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);    /* Check if we can represent this string as a long integer */    len = sdslen(s);    if (len > 21 || !string2l(s,len,&value)) {        /* We can't encode the object...         *         * Do the last try, and at least optimize the SDS string inside         * the string object to require little space, in case there         * is more than 10% of free space at the end of the SDS string.         *         * We do that for larger strings, using the arbitrary value         * of 32 bytes. This code was backported from the unstable branch         * where this is performed when the object is too large to be         * encoded as EMBSTR. */        if (len > 32 &&            o->encoding == REDIS_ENCODING_RAW &&            sdsavail(s) > len/10)        {        //调用sdsRemoveFreeSpace把0->ptr中的字符串中的空格给移除掉            o->ptr = sdsRemoveFreeSpace(o->ptr);        }        /* Return the original object. */        return o;    }    .....
就是移除字符串中的空格所占的空间。想对应也存在一个getDecodeObject(),:

/* Get a decoded version of an encoded object (returned as a new object). * If the object is already raw-encoded just increment the ref count. *//* 获取解码后的robj */robj *getDecodedObject(robj *o) {    robj *dec;    if (o->encoding == REDIS_ENCODING_RAW) {    //如果没有编码方式,则直接增加引用计数,并返回        incrRefCount(o);        return o;    }    if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {        char buf[32];//如果是string,Type而且是encoding_int方式的,先做一步转换        ll2string(buf,32,(long)o->ptr);        dec = createStringObject(buf,strlen(buf));        return dec;    } else {        redisPanic("Unknown encoding type");    }}
以上就是对于对于RedisObject的简单分析。

0 0