Redis通过自己的方法管理内存,主要方法有zmalloc(),zrealloc(),zcalloc()和zfree(),
分别对应C中的malloc(),realloc(),calloc()和free().
redis自己管理内存的好处主要有两个:
1、可以利用内存池等手段提高内存分配的性能;
2、’可以掌握更多的内存信息,以便于redis虚拟内存等功能中,决定何时将数据swap到磁盘。
malloc()分配一块指定大小的内存区域,并返回指向区域开头的指针,若分配失败则返回NULL;
calloc()与malloc()一样分配一块指定大小的内存区域,成功时返回区域头指针,失败时返回NULL。
区别在于,calloc()输入的参数为count何size,即分配的项的数目,及每一项的大小。calloc()
成功分配内存空间后,会将空间内所有值置0.
realloc()修改已分配内存块的 大小。若已分配的内存块后没有足够的空间用于扩展内存块,则
重新申请一块满足需求的内存块,并将旧的数据拷贝到新的位置,释放旧的内存块,返回指向新的
内存块的指针 ;否则直接扩展原有的内存块。若分配失败,返回NULL。
free()释放以分配的内存块。_
内存分配
在redis中,如果系统包含TCMALLOC,则会使用 tc_malloc(0等TCMALLOC中的方法替代malloc()
等原有的分配内存方法。_
static size_t used_memoryy = 0;
static int zmalloc_thread_safe = 0;
void *zmalloc(size_t size) {
/*
redis中除了申请需要的size外还需要申请PREFIX_SIZE大小区域记录内存的长度
redis会使用宏函数updata_zmalloc_stat_alloc()记录申请的内存块的 相关信息
以便监控内存使用状况;当内存块被zfree()释放时,根据头部的信息可以快速
地获知被释放的内存区域的长度,然后通过宏函数update_zmalloc_stat_free()标记
释放。源代码中,若系统支持malloc_size()方法,则会使用它返回指针所指向的内存块
的大小(Mac OS X 10.4以上支持该方法[3])。
*/
void *ptr = malloc(size+PREFIX_SIZE);
if (!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_alloc(zmalloc_size(ptr));
return ptr;
#else
*((size_t*)ptr) = size;
update_zmalloc_stat_alloc(size+PREFIX_SIZE);
return (char*)ptr+PREFIX_SIZE;
#endif
}
/*
有疑惑的是,在支持malloc_size()的系统中,为何还要多申请PREFIX_SIZE的内存?
#ifdef HAVE_MALLOC_SIZE
#define PREFIX_SIZE (0) //此时PREFIX_SIZE大小设置为0
#if defined(__sun) || defined(__sparc) || defined(__sparc__)
#define PREFIX_SIZE (sizeof(long long))
#else
#define PREFIX_SIZE (sizeof(size_t))
#endif
#endif
*/
在updata_zmalloc_stat_alloc(_n) 中首先将要分配的空间与内存对齐,然后会
根据宏zmalloc_thread_safe判断是否需要对内存信息记录表的相关操作加锁。虽然
Redis在大部分场景中是单线程读写的,即thread_safe的,但启用虚拟内存(VM),或持
久化dump到磁盘等操作时会启动多线程,因此在多线程模式中,需要对部分操作加锁。
#define update_zmalloc_stat_alloc(__n) do { \
size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
if (zmalloc_thread_safe) { \
update_zmalloc_stat_add(_n); \
} else { \
used_memory += _n; \
} \
} while(0)
若是内存分配失败时则会调用自己实现的内存分配失败函数,抛出异常
static void zmalloc_default_oom(size_t size) {
fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
size);
fflush(stderr);
abort();
}