本笔记参考《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
设置的上限时,空转时长较搞的那部分键会被服务器释放。