Redis对象——内存回收,对象共享和空转时长

2023-12-12 23:15

本文主要是介绍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对象——内存回收,对象共享和空转时长的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/486281

相关文章

JSON字符串转成java的Map对象详细步骤

《JSON字符串转成java的Map对象详细步骤》:本文主要介绍如何将JSON字符串转换为Java对象的步骤,包括定义Element类、使用Jackson库解析JSON和添加依赖,文中通过代码介绍... 目录步骤 1: 定义 Element 类步骤 2: 使用 Jackson 库解析 jsON步骤 3: 添

Redis的Zset类型及相关命令详细讲解

《Redis的Zset类型及相关命令详细讲解》:本文主要介绍Redis的Zset类型及相关命令的相关资料,有序集合Zset是一种Redis数据结构,它类似于集合Set,但每个元素都有一个关联的分数... 目录Zset简介ZADDZCARDZCOUNTZRANGEZREVRANGEZRANGEBYSCOREZ

Java循环创建对象内存溢出的解决方法

《Java循环创建对象内存溢出的解决方法》在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError),所以本文给大家介绍了Java循环创建对象... 目录问题1. 解决方案2. 示例代码2.1 原始版本(可能导致内存溢出)2.2 修改后的版本问题在

大数据小内存排序问题如何巧妙解决

《大数据小内存排序问题如何巧妙解决》文章介绍了大数据小内存排序的三种方法:数据库排序、分治法和位图法,数据库排序简单但速度慢,对设备要求高;分治法高效但实现复杂;位图法可读性差,但存储空间受限... 目录三种方法:方法概要数据库排序(http://www.chinasem.cn对数据库设备要求较高)分治法(常

Redis多种内存淘汰策略及配置技巧分享

《Redis多种内存淘汰策略及配置技巧分享》本文介绍了Redis内存满时的淘汰机制,包括内存淘汰机制的概念,Redis提供的8种淘汰策略(如noeviction、volatile-lru等)及其适用场... 目录前言一、什么是 Redis 的内存淘汰机制?二、Redis 内存淘汰策略1. pythonnoe

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis延迟队列的实现示例

《Redis延迟队列的实现示例》Redis延迟队列是一种使用Redis实现的消息队列,本文主要介绍了Redis延迟队列的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录一、什么是 Redis 延迟队列二、实现原理三、Java 代码示例四、注意事项五、使用 Redi