Redis内存管理(一)
Redis数据库的内存管理函数有关的文件为:zmalloc.h和zmalloc.c。
Redis作者在编写内存管理模块时考虑到了查看系统内是否安装了TCMalloc或者Jemalloc模块,这两个是已经存在很久的内存管理模块,代码稳定、性能优异,如果已经安装的话,则使用之,最后检查是否是Mac系统,如果是Mac系统的话加载的文件不同,额,本人没进行过Mac编程,这块儿不考虑。对应的源代码为:
1 //检查是否定义了TCMalloc,TCMalloc(Thread-Caching Malloc)与标准glibc库的malloc实现一样的功能,但是TCMalloc在效率和速度效率都比标准malloc高很多 2 #if defined(USE_TCMALLOC) 3 #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 4 #include <google/tcmalloc.h> 5 6 #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 7 #define HAVE_MALLOC_SIZE 1 8 #define zmalloc_size(p) tc_malloc_size(p) 9 #else 10 #error "Newer version of tcmalloc required" 11 #endif 12 //检查是否定义了jemalloc库 13 #elif defined(USE_JEMALLOC) 14 #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 15 #include <jemalloc/jemalloc.h> 16 #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 17 #define HAVE_MALLOC_SIZE 1 18 #define zmalloc_size(p) je_malloc_usable_size(p) 19 #else 20 #error "Newer version of jemalloc required" 21 #endif 22 //检查是否是苹果系统 23 #elif defined(__APPLE__) 24 #include <malloc/malloc.h> 25 #define HAVE_MALLOC_SIZE 1/*标记已经找到了可用的现成函数库*/ 26 #define zmalloc_size(p) malloc_size(p)/*因为每个库的实现方式不一样,所以测试大小的函数也不一样*/ 27 #endif
先整体说一下内存管理的所有接口函数,然后慢慢分析,所有的接口函数为:
1 void *zmalloc(size_t size);//重写malloc函数,申请内存 2 void *zcalloc(size_t size);//重写calloc函数,不再支持按块的成倍申请,内部调用的是zmalloc 3 void *zrealloc(void *ptr, size_t size); //重写内存扩展函数 4 void zfree(void *ptr); //重写内存释放函数,释放时会更新已使用内存的值,如果在多线程下没有开启线程安全模式,可能会出现并发错误。 5 char *zstrdup(const char *s); //字符串持久化存储函数函数,为字符串在堆内分配内存。 6 size_t zmalloc_used_memory(void); //获取已经使用内存大小函数。 7 void zmalloc_enable_thread_safeness(void); //设置内存管理为多线程安全模式,设置之后在更新已使用内存大小时会用Mutex进行互斥操作。 8 void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); //设置内存异常时调用的函数。 9 float zmalloc_get_fragmentation_ratio(size_t rss); // 10 size_t zmalloc_get_rss(void); //获取进程可使用的所有内存大小。 11 size_t zmalloc_get_private_dirty(void); // 12 size_t zmalloc_get_smap_bytes_by_field(char *field); 13 void zlibc_free(void *ptr); //释放指针指向内存函数,用这个释放内存时,不会更新使用内存变量的值。 14 #ifndef HAVE_MALLOC_SIZE 15 size_t zmalloc_size(void *ptr); //获取内存块总题大小函数。 16 #endif
在解析函数的详细实现前,先分析一下.c文件开始所做的处理。在.c文件中定义了三个变量,一个用来记录已经使用内存的大小,一个用来记录是否开启了多线程安全模式,一个是互斥锁。
static size_t used_memory = 0;/*记录已经使用碓内存的大小*/ static int zmalloc_thread_safe = 0;/*是否开启了线程安全*/ pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;/*互斥锁,如果开启了多线程安全,而编译器又不支持原子操作的函数,则需要用互斥锁来完成代码的互斥操作。*/
接下来要确定内存块前面是否需要加个头部,因为不能确定具体使用的到底是什么内存分配函数,TCMalloc或者Jemalloc库在申请的内存块前增加了一个小块的内存来记录该内存块的使用情况,因为本人没分析过着俩库,具体的不做分析,具体的代码为:
#ifdef HAVE_MALLOC_SIZE//找到了已有的内存管理库时就会定义这个宏,如果已经定义了,则不再增加头部大小,如果没有定义,则根据具体的系统来确定增加头部大小的长度。 #define PREFIX_SIZE (0)//头部长度为0 #else #if defined(__sun) || defined(__sparc) || defined(__sparc__) #define PREFIX_SIZE (sizeof(long long))//头部长度为longlong的大小 #else #define PREFIX_SIZE (sizeof(size_t))//头部长度为size_tde的大小 #endif #endif
接下来是定义了两个更新内存占用变量的操作函数update_zmalloc_stat_add和update_zmalloc_stat_sub,这俩函数支持多线程。
1 /*先检查编译器是否支持原子操作函数,如果支持的话,就不用互斥锁了,毕竟锁的效率很低。*/ 2 #if defined(__ATOMIC_RELAXED)//这个好像是C++11标准开始支持的,具体的不是很清楚 3 #define update_zmalloc_stat_add(__n) __atomic_add_fetch(&used_memory, (__n), __ATOMIC_RELAXED) 4 #define update_zmalloc_stat_sub(__n) __atomic_sub_fetch(&used_memory, (__n), __ATOMIC_RELAXED) 5 #elif defined(HAVE_ATOMIC)//这个是GCC从某个版本开始支持的,不是所有版本的GCC都支持sync系列函数。 6 #define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n)) 7 #define update_zmalloc_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n)) 8 #else//不支持原子操作的情况下只能使用互斥锁了。 9 #define update_zmalloc_stat_add(__n) do { \ 10 pthread_mutex_lock(&used_memory_mutex); \ 11 used_memory += (__n); \ 12 pthread_mutex_unlock(&used_memory_mutex); \ 13 } while(0) 14 15 #define update_zmalloc_stat_sub(__n) do { \ 16 pthread_mutex_lock(&used_memory_mutex); \ 17 used_memory -= (__n); \ 18 pthread_mutex_unlock(&used_memory_mutex); \ 19 } while(0) 20 21 #endif 22 //更新内存使用变量函数,函数内确定是否需要多线程安全 23 #define update_zmalloc_stat_alloc(__n) do { \ 24 size_t _n = (__n); \ 25 if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ 26 if (zmalloc_thread_safe) { \ 27 update_zmalloc_stat_add(_n); \ 28 } else { \ 29 used_memory += _n; \ 30 } \ 31 } while(0) 32 //更新内存使用变量函数,函数内确定是否需要多线程安全 33 #define update_zmalloc_stat_free(__n) do 34 { \ 35 size_t _n = (__n); \ 36 if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ 37 if (zmalloc_thread_safe) 38 { \ 39 update_zmalloc_stat_sub(_n); \ 40 } 41 else 42 { \ 43 used_memory -= _n; \ 44 } \ 45 } while(0)
下一篇再写具体的函数实现。