在上一节介绍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());