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

相关文章

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

离心萃取机废旧磷酸铁锂电池回收工艺流程

在废旧磷酸铁锂电池的回收工艺流程中,离心萃取机主要应用于萃取除杂的步骤,以提高回收过程中有价金属(如锂)的纯度。以下是结合离心萃取机应用的废旧磷酸铁锂电池回收工艺流程: 电池拆解与预处理 拆解:将废旧磷酸铁锂电池进行拆解,分离出电池壳、正负极片、隔膜等部分。破碎与筛分:将正负极片进行破碎处理,并通过筛分将不同粒径的物料分开,以便后续处理。 浸出与溶解 浸出:采用适当的浸出工艺(如二段式逆

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

怎么让1台电脑共享给7人同时流畅设计

在当今的创意设计与数字内容生产领域,图形工作站以其强大的计算能力、专业的图形处理能力和稳定的系统性能,成为了众多设计师、动画师、视频编辑师等创意工作者的必备工具。 设计团队面临资源有限,比如只有一台高性能电脑时,如何高效地让七人同时流畅地进行设计工作,便成为了一个亟待解决的问题。 一、硬件升级与配置 1.高性能处理器(CPU):选择多核、高线程的处理器,例如Intel的至强系列或AMD的Ry

Java第二阶段---09类和对象---第三节 构造方法

第三节 构造方法 1.概念 构造方法是一种特殊的方法,主要用于创建对象以及完成对象的属性初始化操作。构造方法不能被对象调用。 2.语法 //[]中内容可有可无 访问修饰符 类名([参数列表]){ } 3.示例 public class Car {     //车特征(属性)     public String name;//车名   可以直接拿来用 说明它有初始值     pu

JVM内存调优原则及几种JVM内存调优方法

JVM内存调优原则及几种JVM内存调优方法 1、堆大小设置。 2、回收器选择。   1、在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提供的内存查看工具,比如JConsole和Java VisualVM。   2、对JVM内存的系统级的调优主要的目的是减少

JVM 常见异常及内存诊断

栈内存溢出 栈内存大小设置:-Xss size 默认除了window以外的所有操作系统默认情况大小为 1MB,window 的默认大小依赖于虚拟机内存。 栈帧过多导致栈内存溢出 下述示例代码,由于递归深度没有限制且没有设置出口,每次方法的调用都会产生一个栈帧导致了创建的栈帧过多,而导致内存溢出(StackOverflowError)。 示例代码: 运行结果: 栈帧过大导致栈内存

Redis中使用布隆过滤器解决缓存穿透问题

一、缓存穿透(失效)问题 缓存穿透是指查询一个一定不存在的数据,由于缓存中没有命中,会去数据库中查询,而数据库中也没有该数据,并且每次查询都不会命中缓存,从而每次请求都直接打到了数据库上,这会给数据库带来巨大压力。 二、布隆过滤器原理 布隆过滤器(Bloom Filter)是一种空间效率很高的随机数据结构,它利用多个不同的哈希函数将一个元素映射到一个位数组中的多个位置,并将这些位置的值置

理解java虚拟机内存收集

学习《深入理解Java虚拟机》时个人的理解笔记 1、为什么要去了解垃圾收集和内存回收技术? 当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就必须对这些“自动化”的技术实施必要的监控和调节。 2、“哲学三问”内存收集 what?when?how? 那些内存需要回收?什么时候回收?如何回收? 这是一个整体的问题,确定了什么状态的内存可以

# VMware 共享文件

VMware tools快速安装 VMware 提供了 open-vm-tools,这是 VMware 官方推荐的开源工具包,通常不需要手动安装 VMware Tools,因为大多数 Linux 发行版(包括 Ubuntu、CentOS 等)都包含了 open-vm-tools,并且已经优化以提供与 VMware 环境的兼容性和功能支持。 建议按照以下步骤安装 open-vm-tools 而不