本节探讨下 RedisModule_OpenKey
中 key-value 何时释放。
首先 的数据结构如下
struct RedisModuleKey {
....
robj *key;
robj *value;
....
}
我们关心的是 RedisModuleKey
本身以及 其中 的 key和value值的生命周期
RedisModuleKey 本身 通过 RedisModule_OpenKey
申请,模块内存管理自动将 RedisModuleKey
保存在ctx中。在指令执行完成之后,调用moduleFreeContext->autoMemoryCollect->RM_CloseKey->moduleCloseKey
尝试进行释放。
可以看到 一次完整的命令,RedisModuleKey 本身会被无情释放。
void RM_CloseKey(RedisModuleKey *key) {
if (key == NULL) return;
moduleCloseKey(key);
autoMemoryFreed(key->ctx,REDISMODULE_AM_KEY,key);
zfree(key);//彻底释放
}
所以,RedisModule_OpenKey
的本质可以理解为只是一个临时变量
,
现在我们来看RedisModuleKey其中的 key值,通过下面可以看到,key的引用计数减1。
/* Destroy a RedisModuleKey struct (freeing is the responsibility of the caller). */
static void moduleCloseKey(RedisModuleKey *key) {
int signal = SHOULD_SIGNAL_MODIFIED_KEYS(key->ctx);
if ((key->mode & REDISMODULE_WRITE) && signal)
signalModifiedKey(key->ctx->client,key->db,key->key);
/* TODO: if (key->iter) RM_KeyIteratorStop(kp); */
RM_ZsetRangeStop(key);
decrRefCount(key->key);
}
所以key会被无情释放么?不一定,如果 RedisModuleKey
入参的 key 引用计数为1且模式是write模式,那么在 RM_OpenKey->moduleInitKey
中,引用计数会被++。也就是RedisModule_OpenKey
返回后,key的引用计数在原先的基础上加1,那么在 moduleCloseKey中,key就不会被释放。
如果 如果大家这么用:RedisModule_OpenKey(ctx, argv[1]...
,即喜欢把用户的入参作为open的参数,虽然 RedisModule_OpenKey
返回后 argv[1]
的引用计数是2,但是arg参数本身会在redis框架最外层释放一次,所以argv[1] 不会被保留在内存里面。
但是 如果你这么用:RedisModule_OpenKey(ctx, RedisModule_CreateString(NULL, s, (size_t)slen)...)
,那么 没人会释放 RedisModule_CreateString
创建的内存,此时要么 RedisModule_CreateString
第一个入参传入ctx然后依靠redis自动内存管理来自动释放内存,要么自己主动调用free来减引用计数。
再来看看value值,module通过RedisModule_ModuleTypeSetValue
来设置 value值,其本质就是genericSetKey
,将 RedisModuleKey
中的key-value 保存到redis的db中,
int RM_ModuleTypeSetValue(RedisModuleKey *key, moduleType *mt, void *value) {
if (!(key->mode & REDISMODULE_WRITE) || key->iter) return REDISMODULE_ERR;
/*旧的value如果存在,例如你之前执行过此函数,旧的value引用计数会被减1*/
RM_DeleteKey(key);
robj *o = createModuleObject(mt,value);
genericSetKey(key->ctx->client,key->db,key->key,o,0,0);
/*到这 o的应用计数为2*/
decrRefCount(o);
/*到这 o的应用计数为1*/
key->value = o;
return REDISMODULE_OK;
}
所以,value值的引用计数,在 通过 RedisModule_ModuleTypeSetValue
后并没有增减。也就是value值的生命周期没有改变。
RedisModule_DeleteKey
其实就是 dbDelete
的封装,RedisModule_ModuleTypeSetValue
将key-value保存在db中,然后并没有增加减少他们的引用计数,RedisModule_DeleteKey
因为调用了dbDelete
所以会直接将key以及value的引用计数减1。
总结下:
1、RedisModule_OpenKey 返回的RedisModuleKey指针,无论如何都会被释放。其中,RedisModule_OpenKey 会对入参的key,引用计数加一。然后在释放 RedisModuleKey时,减1,如果其他地方需要用这个 如果 入参key原先引用计数为1,那么需要额外的操作才能将其释放。
2、如果进行了 RedisModule_ModuleTypeSetValue
,那么,RedisModuleKey总的key和value被存在db,其中,key 是拷贝一份的,我们所有不关心。value被db直接引用了(指针直接指向了value)。RedisModule_ModuleTypeSetValue
本身不会对key和value引用计数进行增删。
3、通过DEL命令,删除 RedisModule_OpenKey
中指定的key时,和标准流程一样,对 value引用计数减1,RedisModule_DeleteKey
也是一样的逻辑。