【Redis】Redis对象——内存回收,对象共享和空转时长
一. 内存回收
因为C语言不具备内存回收功能,所以Redis在自己的对象系统中构建了一个引用计数技术实现内存回收机制。通过这一机制,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收。
内每一个对象的引用计数信息由redisObject结构的refcount属性记录:
typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ int refcount; void *ptr; } robj;
对象的引用计数信息会随着对象的使用状态而不断变化:
在创建一个新对象时,引用计数的值会被初始化为1。
当对象被一个新程序使用时,他的引用计数会被增1。
当对象不被程序使用时,他的引用计数会被减1。
当对象的引用计数值变为0时,对象所占有的内存会被释放掉。
下面是对引用计数进行操作的接口:
//创建对象 robj *createObject(int type, void *ptr); robj *createStringObject(const char *ptr, size_t len); robj *createRawStringObject(const char *ptr, size_t len); robj *createEmbeddedStringObject(const char *ptr, size_t len); robj *createStringObjectFromLongLong(long long value); robj *createStringObjectFromLongLongForValue(long long value); robj *createStringObjectFromLongDouble(long double value, int humanfriendly); robj *createQuicklistObject(void); robj *createZiplistObject(void); robj *createSetObject(void); robj *createIntsetObject(void); robj *createHashObject(void); robj *createZsetObject(void); robj *createZsetZiplistObject(void); robj *createStreamObject(void); robj *createModuleObject(moduleType *mt, void *value); //对引用计数加1 void incrRefCount(robj *o) { if (o->refcount < OBJ_FIRST_SPECIAL_REFCOUNT) { o->refcount++; } else { if (o->refcount == OBJ_SHARED_REFCOUNT) { /* Nothing to do: this refcount is immutable. */ } else if (o->refcount == OBJ_STATIC_REFCOUNT) { serverPanic("You tried to retain an object allocated in the stack"); } } } //对引用计数减1,减为0时释放对象内存 void decrRefCount(robj *o) { if (o->refcount == 1) { switch(o->type) { case OBJ_STRING: freeStringObject(o); break; case OBJ_LIST: freeListObject(o); break; case OBJ_SET: freeSetObject(o); break; case OBJ_ZSET: freeZsetObject(o); break; case OBJ_HASH: freeHashObject(o); break; case OBJ_MODULE: freeModuleObject(o); break; case OBJ_STREAM: freeStreamObject(o); break; default: serverPanic("Unknown object type"); break; } zfree(o); } else { if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0"); if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--; } }
对象的整个生命周期可以划分为创建对象,操作对象,释放对象三个阶段。
二. 对象共享
Redis除了用于实现引用计数内存回收机制之外,对象的引用计数属性还带由对象共享的作用。
举个例子:假设键A创建一个包含整数值100的字符串对象作为值对象,如果这时键B也要创建一个同样保存了整数值100的字符串对象作为值对象。Redis服务器的做法是,让键A和键B共享同一个字符串对象。也就是将数据库的键的值指针指向一个现有的值对象,将共享的值对象的引用计数加一。
这样的共享对象机制的作用是节约内存。
目前来说,Redis会在初始化服务器时,创建一万个字符串对象,这些对象包含从0到9999的所有整数值,当服务器需要用到值为0到9999的字符串对象时,服务器会使用到这些共享对象。而不是新创建对象。
注意:创建共享字符串对象的数量可以通过redis.h/REDIS_SHARED_INTEGERS常量修改。
下面的命令佐证了只有值是0到9999整数的字符串对象作为共享对象:
127.0.0.1:6379> set A 10000 OK 127.0.0.1:6379> OBJECT REFCOUNT A (integer) 1 127.0.0.1:6379> set B 10000 OK 127.0.0.1:6379> OBJECT REFCOUNT A (integer) 1 127.0.0.1:6379> OBJECT REFCOUNT B (integer) 1
另外,这些共享对象不单单只有字符串键可以使用,那些在数据结构嵌套了字符串对象的对象(linkedlist编码的列表对象,hashtable编码的哈希对象,hashtable编码的集合对象,zset编码的有序集合对象)都可以使用这些共享对象。
为什么Redis不包含字符串的对象:
当服务器考虑将一个共享对象设置为键的值对象时,程序需要先检查给定的共享对象和键想创建的目标对象是否完全相同,只有在共享对象和目标对象完全相同的情况下,程序才会将共享对象用作键的值对象。当共享对象保存的值越复杂,验证共享对象和目标对象是否相同所需要的复杂度越高。
假如共享对象是整数值的字符串对象,那么验证操作的复杂度为O(1)。
假如共享对象是字符串值的字符串对象,那么验证操作复杂度为O(N)。
假如共享对象是包含了多个值(或对象)的对象,比如列表对象或哈希对象,那么验证操作的复杂度将会是O(N^2)。
因此,尽管共享对象可以节约内存,但是为了效率着想,Redis只对包含整数值的字符串对象进行共享。
三. 对象时间空转时长
redisObject对象中还有一个属性lru,记录了对象最后一次被命令程序访问的时间。OBJECT IDLETIME命令可以打印出给定键的空转时长,这个空转时长就是通过将当前时间减去键的值对象的lru时间计算出来的。OBJECT IDLETIME命令在执行时不会修改lru属性。
对象的空转时长出来可以使用OBJECT IDLETIME命令查看外,另外一个作用是:如果服务器打开了maxmemory选项,并且服务器的回收算法为volatile-lru或者allkeys-lru,那么当服务器占用的内存数超过maxmemory的值时,空转时长较高的那部分键会被服务器优先释放,从而回收内存。
猜你喜欢
- 【Redis】redis分布式缓存
- 目录一、单机安装Redis。二、Redis主从集群。2.1.集群结构2.2.准备实例和配置2.3.启动2.4.开启主从关系2.5.测试三、搭建哨兵集群。3.1.集群结构3.2.准备实例和配置3.3.启动3.4.测试四、搭建分片集群。4.1.集群结构4.2.准备实例和配置4.3.启动4.4.创建集群4.5.测试五、单点redis的问题。六、Redis持久化。1)RDB持久化。2) AOF持久化。3)RDB和AOF的对比。七、Redis主从。1)搭建主从架构。2) 主从数据同步原
- 【Redis】Redis配置详解
- 目录一、Redis查看当前配置命令二、Redis基本配置三、RDB全量持久化配置(默认开启)四、AOF增量持久化配置五、Redis key过期监听配置六、Redis内存淘汰策略七、总结一、Redis查看当前配置命令# Redis查看当前全部配置信息 127.0.0.1:6379> CONFIG GET * # Redis查看当前指定配置信息 127.0.0.1:6379> CONFIG GET 配置名
- 【Redis】Redis对象——内存回收,对象共享和空转时长
- 一. 内存回收 因为C语言不具备内存回收功能,所以Redis在自己的对象系统中构建了一个引用计数技术实现内存回收机制。通过这一机制,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收。 内每一个对象的引用计数信息由redisObject结构的refcount属性记录:typedef struct redisObject {
- 【Redis】Redis与Memcached的区别
- 一、主要区别:1.Redis是一个开源的内存数据结构存储,用作数据库,缓存和消息代理; 2.Memcached是一个免费的开源高性能分布式内存对象缓存系统,它通过减少数据库负载来加速动态Web应用程序。memcached是高性能的分布式内存缓存服务器。 一般使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态web应用的速度、提高可扩展性。redis是一个开源的内存数据库,它以键值对的形式存储数据。由于数据存储在内存中,因此Redis的速度很快,但是每次重启Redis服务时,其中的
- 【Redis】Redis 配置文件详解
- Units 单位配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit,大小写不敏感。# Redis configuration file example. # # Note that in order to read the configuration file, Redis must be # start
- 【Redis】redis除了做缓存还能做什么
- redis应该说是目前最受欢迎的nosql数据库之一了。redis通常被作为缓存组件,用作缓存数据。不过,除了可以缓存数据,其实redis可以做的事还有很多。下面列举几例,供大家参考。1、最新列表例如新闻列表页面最新的新闻列表,如果总数量很大的情况下,尽量不要使用select a from A limit 10,尝试redis的 LPUSH命令构建List,一个个顺序都塞进去就可以啦。不过万一内存清掉了咋办? (推荐学习:Red