在上一节介绍zmalloc/zfree函数时,我们看到redis会调用increment_used_memory/decrement_used_memory,这两个宏其实就是对static变量used_memory进行指定大小的增加/减少,该变量保存了redis所使用的内存大小:
// sizeof(long)字节对齐
#define increment_used_memory(__n) do { \
size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
if (zmalloc_thread_safe) { \
pthread_mutex_lock(&used_memory_mutex); \
used_memory += _n; \
pthread_mutex_unlock(&used_memory_mutex); \
} else { \
used_memory += _n; \
} \
} while(0)
分配内存的线程安全性是由底层系统的malloc系列函数来保证的,而used_memory变量的线程安全性取决于线程锁zmalloc_thread_safe的值。由于redis处理客户端连接是单进程单线程事件多路循环的,那么就有一个疑问,redis在什么情况下,需要多线程保护了?在后续的介绍中,我们会看到,redis的虚拟内存(VM)可能启用多线程。
我们来看看zmalloc_thread_safe 的相关处理。zmalloc_thread_safe 初始值为0,仅在zmalloc_enable_thread_safeness函数中改为1,而zmalloc_enable_thread_safeness仅在全局变量server.vm_max_threads!=0(也即vm启用多线程时),才在vmInit中调用。
void zmalloc_enable_thread_safeness(void) {
zmalloc_thread_safe = 1;
}
static void vmInit(void) {
---
if (server.vm_max_threads != 0)
zmalloc_enable_thread_safeness(); /* we need thread safe zmalloc() */
---
}
内存相关的函数,除了zmalloc、zremalloc、zfree等直接操作内存外,常用的就是使用zmalloc_used_memory返回已分配的内存大小了,redis对内存的监控仅限于此。
size_t zmalloc_used_memory(void) {
size_t um;
if (zmalloc_thread_safe) pthread_mutex_lock(&used_memory_mutex);
um = used_memory;
if (zmalloc_thread_safe) pthread_mutex_unlock(&used_memory_mutex);
return um;
}
redis在某些位置输出log时需要调用该函数输出已分配的内存大小外,比如serverCron中的调用:
redisLog(REDIS_VERBOSE,"%d clients connected (%d slaves), %zu bytes in use",
listLength(server.clients)-listLength(server.slaves),
listLength(server.slaves),
zmalloc_used_memory());