天天看点

Redis对象的refcount与lru属性(内存回收、对象共享、空转时长)内存回收对象共享空转时长

本笔记参考《Redis设计与实现》 P84~P88

内存回收

Redis在对象系统中使用reference counting技术实现了内存回收机制。程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收。

typedef struct redisObject {
	// ...
	// 引用计数
	int refcount;
	// ...
} robj;
           

refcount会随着对象的使用状态而不断变化:

  • 创建一个新对象时,refcount被初始化为1
  • 当对象被一个新程序使用时,refcount++
  • 当对象不再被一个程序使用时,refcount–
  • 当对象引用计数为0时,对象所占内存被释放

一些API:

函数 作用
incrRefCount 引用计数+1
decrRefCount 引用计数-1 ,为0时释放对象
resetRefCount 引用计数设置为0,不释放对象

举例:

// 创建一个字符串对象s,对象引用计数为1
robj *s = createStringObject(...)
// 对象s执行各种操作
...
// 将对象s的引用计数-1,降为0,导致对象被释放
decrRefCount(s)
           

对象共享

引用计数还带有对象共享的作用。

在Redis中,让多个键共享同一个值对象需要执行两个步骤:

1、将数据库键的值指向一个现有的值对象

2、将被共享的值对象的引用计数+1

共享对象机制对于节约内存非常有帮助,数据库中保存的相同的值对象对越,对象共享机制就能节约越多的内存。

Redis会在初始化服务器时,创建一万个字符串对象,包含了0~9999的所有整数值。当服务器有用到这些整数字符串对象,就利用的是共享对象,而非新创建对象。

可以使用

OBJECT REFCOUNT 对象Key

来查看引用计数。

在数据结构中嵌套了字符串对象的独享如(linkedlist编码的列表对象,hashtable编码的哈希对象,hashtable编码的集合对象,zset编码的有序集合对象)

需要注意下面一个问题:

只有共享对象和目标都西昂完全相同时,才会将共享对象的作为键的值对象,所以需要先验证是否相等。

一个共享对象保存的值越复杂,验证是否相等所需要的复杂度就越高:

1、如果是整数型字符串对象,O(1)

2、如果是字符串值的字符串对象,O(n)

3、如果是包含了多个对象的对象,O(n^2)

空转时长

lru属性记录了对象最后一次被命令程序访问的时间

typedef struct redisObject {
	// ...
	// 引用计数
	unsigned lur : 22;
	// ...
} robj;
           

使用

OBJECT IDLETIME

可以打印出键的空转时长(当前时间减去键的值对象的lru时间)。

如果服务器打开了

maxmemory

选项,并且回收内存的算法为

volatile-lru

或者

allkeys-lru

,那么服务器占用的内存数超过

maxmemory

设置的上限时,空转时长较搞的那部分键会被服务器释放。