内存回收 ----------------------------- 因为 C 语言并不具备自动的内存回收功能, 所以 Redis 在自己的对象系统中构建了一个引用计数(\ `reference counting `_\ )技术实现的内存回收机制, 通过这一机制, 程序可以通过跟踪对象的引用计数信息, 在适当的时候自动释放对象并进行内存回收。 每个对象的引用计数信息由 ``redisObject`` 结构的 ``refcount`` 属性记录: :: typedef struct redisObject { // ... // 引用计数 int refcount; // ... } robj; 对象的引用计数信息会随着对象的使用状态而不断变化: - 在创建一个新对象时, 引用计数的值会被初始化为 ``1`` ; - 当对象被一个新程序使用时, 它的引用计数值会被增一; - 当对象不再被一个程序使用时, 它的引用计数值会被减一; - 当对象的引用计数值变为 ``0`` 时, 对象所占用的内存会被释放。 表 8-12 列出了修改对象引用计数的 API , 这些 API 分别用于增加、减少、重置对象的引用计数。 ------------------------------------------------------------------------------------- 表 8-12 修改对象引用计数的 API +-------------------+---------------------------------------------------------------+ | 函数 | 作用 | +===================+===============================================================+ | ``incrRefCount`` | 将对象的引用计数值增一。 | +-------------------+---------------------------------------------------------------+ | ``decrRefCount`` | 将对象的引用计数值减一, | | | 当对象的引用计数值等于 ``0`` 时, | | | 释放对象。 | +-------------------+---------------------------------------------------------------+ | ``resetRefCount`` | 将对象的引用计数值设置为 ``0`` , | | | 但并不释放对象, | | | 这个函数通常在需要重新设置对象的引用计数值时使用。 | +-------------------+---------------------------------------------------------------+ ------------------------------------------------------------------------------------- 对象的整个生命周期可以划分为创建对象、操作对象、释放对象三个阶段。 作为例子, 以下代码展示了一个字符串对象从创建到释放的整个过程: :: // 创建一个字符串对象 s ,对象的引用计数为 1 robj *s = createStringObject(...) // 对象 s 执行各种操作 ... // 将对象 s 的引用计数减一,使得对象的引用计数变为 0 // 导致对象 s 被释放 decrRefCount(s) 其他不同类型的对象也会经历类似的过程。 .. 作为例子, 图 IMAGE_EXAMPLE 展示了一个引用计数为 ``1`` 的字符串对象。 .. graphviz:: digraph { label = "\n 图 IMAGE_EXAMPLE 带有引用计数的字符串对象"; rankdir = LR; node [shape = record]; redisObject [label = " redisObject | type \n REDIS_STRING | encoding \n REDIS_ENCODING_INT | ptr | refcount \n 1 | ... "]; node [shape = plaintext]; number [label = "10086"] redisObject:ptr -> number; } 表 TABLE_CREATE_OBJECT 展示了 Redis 的对象系统中, 所有创建对象的 API 。 -------------------------------------------------------------------------------------------------------------------------- 表 TABLE_CREATE_OBJECT 创建对象的 API +----------------+---------------------------------------+---------------------------------------------------------------+ | 创建对象的类型 | 函数 | 作用 | +================+=======================================+===============================================================+ | 字符串对象 | ``createStringObject`` | 创建一个字符串对象, | | | | 对象的编码根据字符串长度绝度: | | | | 小于等于 ``32`` 字节的使用 ``embstr`` 编码, | | | | 其他大小使用 ``raw`` 编码。 | | +---------------------------------------+---------------------------------------------------------------+ | | ``createStringObjectFromLongLong`` | 创建一个字符串对象, | | | | 对象的编码根据输入整数的大小决定: | | | | 如果输入整数可以用 ``long`` 类型表示, | | | | 那么使用 ``int`` 编码; | | | | 如果输入整数需要使用 ``long long`` 类型表示, | | | | 那么使用 ``raw`` 编码。 | | +---------------------------------------+---------------------------------------------------------------+ | | ``createStringObjectFromLongDouble`` | 创建一个字符串对象, | | | | 对象的编码根据输入浮点数转换成字符串之后的长度决定, | | | | 条件和 ``createStringObject`` 函数一样。 | | +---------------------------------------+---------------------------------------------------------------+ | | ``dupStringObject`` | 复制一个字符串对象,对象的拷贝和源对象的编码类型保持一致。 | +----------------+---------------------------------------+---------------------------------------------------------------+ | 列表对象 | ``createListObject`` | 创建一个 ``linkedlist`` 编码的列表对象。 | | +---------------------------------------+---------------------------------------------------------------+ | | ``createZiplistObject`` | 创建一个 ``ziplist`` 编码的列表对象。 | +----------------+---------------------------------------+---------------------------------------------------------------+ | 集合对象 | ``createSetObject`` | 创建一个 ``hashtable`` 编码的集合对象。 | | +---------------------------------------+---------------------------------------------------------------+ | | ``createIntsetObject`` | 创建一个 ``intset`` 编码的集合对象。 | +----------------+---------------------------------------+---------------------------------------------------------------+ | 哈希对象 | ``createHashObject`` | 创建一个 ``ziplist`` 编码的哈希对象。 | +----------------+---------------------------------------+---------------------------------------------------------------+ | 有序集合对象 | ``createZsetObject`` | 创建一个 ``skiplist`` 编码的有序集合对象。 | | +---------------------------------------+---------------------------------------------------------------+ | | ``createZsetZiplistObject`` | 创建一个 ``ziplist`` 编码的有序集合对象。 | +----------------+---------------------------------------+---------------------------------------------------------------+ -------------------------------------------------------------------------------------------------------------------------- 表 TABLE_CREATE_OBJECT 并没有给出创建 ``hashtable`` 编码的哈希对象的 API , 因为 Redis 没有将这一操作抽象成函数, 而是直接写在了哈希对象的编码转换程序里面。 表 8-12 列出了修改对象引用计数的 API , 这些 API 分别用于增加、减少、重置对象的引用计数。 ------------------------------------------------------------------------------------- 表 8-12 修改对象引用计数的 API +-------------------+---------------------------------------------------------------+ | 函数 | 作用 | +===================+===============================================================+ | ``incrRefCount`` | 将对象 ``refcount`` 属性的值增一。 | +-------------------+---------------------------------------------------------------+ | ``decrRefCount`` | 将对象 ``refcount`` 属性的值减一, | | | 当 ``refcount`` 的值为 ``0`` 时, | | | 释放对象。 | +-------------------+---------------------------------------------------------------+ | ``resetRefCount`` | 将对象 ``refcount`` 属性的值设置为 ``0`` , | | | 但并不释放对象, | | | 这个函数通常在需要重新设置对象的 ``refcount`` 属性时使用。 | +-------------------+---------------------------------------------------------------+ ------------------------------------------------------------------------------------- ``resetRefCount`` 函数是多态的, 它会对传入对象的类型进行检查, 并根据对象的类型来调用正确的函数来释放对象。 表 TABLE_RELEASE_OBJECT 展示了 ``resetRefCount`` 函数在释放不同类型的对象时调用的函数。 ------------------------------------------------- 表 TABLE_RELEASE_OBJECT 释放对象的 API +-----------------------+-----------------------+ | 函数 | 作用 | +=======================+=======================+ | ``freeStringObject`` | 释放字符串对象。 | +-----------------------+-----------------------+ | ``freeListObject`` | 释放列表对象。 | +-----------------------+-----------------------+ | ``freeHashObject`` | 释放哈希对象。 | +-----------------------+-----------------------+ | ``freeSetObject`` | 释放集合对象。 | +-----------------------+-----------------------+ | ``freeZsetObject`` | 释放有序集合对象。 | +-----------------------+-----------------------+ ------------------------------------------------- :: # 演示一段对象从创建到结束的示例代码