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


              今天的学习效率比较高,把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的简单分析。
  • 相关阅读:
    java中日期格式的转换和应用
    struts2下载
    struts2上传
    div下拉框(待改善)
    jsp中jquery用法一步刷新 验证用户名是否存在
    react 解析json
    babel-polyfill 解决浏览器兼容
    页面跳转缓存 keepAlive
    webpack知识点
    element-ui upload
  • 原文地址:https://www.cnblogs.com/bianqi/p/12184203.html
Copyright © 2020-2023  润新知