本文主要是介绍redis内存分配分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
简述
redis的内存分配相关的代码存储与zmalloc.h
和zmalloc.c
之中,整体的分配策略非常的简单,需要额外注意HAVE_MALLOC_SIZE
这个宏
zmalloc.h
在这里,一开始让我很疑惑的是开头的这一系列条件编译
#if defined(USE_TCMALLOC)#define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))#include <google/tcmalloc.h>#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)#define HAVE_MALLOC_SIZE 1#define zmalloc_size(p) tc_malloc_size(p)#else#error "Newer version of tcmalloc required"#endif#elif defined(USE_JEMALLOC)#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))#include <jemalloc/jemalloc.h>#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)#define HAVE_MALLOC_SIZE 1#define zmalloc_size(p) je_malloc_usable_size(p)#else#error "Newer version of jemalloc required"#endif#elif defined(__APPLE__)#include <malloc/malloc.h>#define HAVE_MALLOC_SIZE 1#define zmalloc_size(p) malloc_size(p)
#endif
后来认真啃了下网上相关的文章才明白了,redis
根据这一系列的条件编译,来决定使用哪种内存分配方案,即tcmalloc,jemalloc,malloc(Apple)
。第三个的malloc
是OS X
系统。
可以注意到,在条件编译中, redis
均定义了HAVE_MALLOC_SIZE
这个宏。原因是,这三种内存分配方案都提供了计算所分配内存大小的函数,如果不使用这三种方案,单纯的使用C自带的malloc
的话,需要自己去倒腾计算内存大小的函数。redis
将这三种内存分配方案中计算内存大小的函数都换成了统一的zmalloc_size
。这也是为了方便以后的使用
zmalloc.c
这里便是内存分配的实现了,花了我好一阵时间去啃,redis
确实用到了蛮厉害的技巧。
首先该文件引用了config.h
和pthread.h
俩头文件,config.h
包含了一些跨平台的宏,pthread.h
则包含了多进程所需锁。
宏
#ifdef HAVE_MALLOC_SIZE#define PREFIX_SIZE (0)
#else#if defined(__sun) || defined(__sparc) || defined(__sparc__)#define PREFIX_SIZE (sizeof(long long))#else#define PREFIX_SIZE (sizeof(size_t))#endif
#endif
需要重点关注的便是这里所出现的宏了。HAVE_MALLOC_SIZE
这个宏如果被定义过了,说明使用了内存分配方案中有mallloc_size
这个函数,能够给出分配的内存大小,否则redis
定义了PREFIX_SIZE
这一变量,该变量用于在分配内存时,额外的划分一段内存,用于存储本次所分配的内存大小。
其次是used_memory
这一静态变量,该变量用于存储进程所占用的内存大小,每次对内存进行增删的时候,都使用宏来修改该值。使用宏而不使用函数,估计是为了增加运行效率。
- 增加内存时,使用
update_zmalloc_stat_alloc
来增加used_memory
的值 - 减少内存时,使用
update_zmalloc_stat_free
来减少used_memory
的值
在这俩函数中,需要额外注意的是
if (_n&(sizeof(long)-1))_n += sizeof(long)-(_n&(sizeof(long)-1));
其中if
用于判断本次内存的变化量是否是long类型
大小的整数倍,如果不是,则进行一个对齐处理,而后通过update_zmalloc_stat_add
安全的增加used_memory
的值。
函数实现
redis
有俩实现分配内存的函数,分别是zmalloc
和zcalloc
,这俩函数看名字就知道区别了。
在分配内存的时候,除了分配参数所要求的大小的内存外,还需额外的分配上文提到的PREFIX_SIZE
大小的内存空间,如果定义了HAVE_MALLOC_SIZE
PREFIX_SIZE的值是0,不需要偏移。如果使用的是C自带的malloc
,还需要额外的一小步工作
*((size_t*)ptr) = size;update_zmalloc_stat_alloc(size+PREFIX_SIZE);return (char*)ptr+PREFIX_SIZE;
通过第一条语句,将指针所指的地方(此时还在开头),存放本次内存分配的大小,而后去更新used_memory
。记得返回值要偏移PREFIX_SIZE
个字节
如果使用C自带的malloc
,则还需要额外的定义一个自己的zmalloc_size
。
在搞懂上述一个函数之后,其他的函数便不难理解了。
zmalloc_used_memory
这个函数用于返回进程所占用的内存大小,要注意到获取used_memory
时,都有上一个锁,推测是是为了防止在获取大小的时候,其他进程修改了值。
最后需要注意的是zmalloc_get_rss
这个函数用于返回实际占用的物理内存大小,由注释可以看出,这是通过特定的系统调用获取到准确的内存占用。
具体的可以参考这篇博文写的十分清楚。此外zmalloc_get_private_dirty
也同样直接参考该博文即可。
这篇关于redis内存分配分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!