您的位置:首页 > 数据库 > Redis

Redis 源码阅读笔记1:zmalloc

2017-01-15 23:57 666 查看

Redis 源码阅读笔记1:zmalloc

zmalloc 主要用于Redis的内存管理,实际上它是在tcmalloc和jemalloc以及malloc的基础上进行了重新封装,所以并不是全新的内存管理体系。

所有函数如下所列

void *zmalloc(size_t size);
void *zcalloc(size_t size);
void *zrealloc(void *ptr, size_t size);
void zfree(void *ptr);
char *zstrdup(const char *s);
size_t zmalloc_used_memory(void);
void zmalloc_enable_thread_safeness(void);
void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
float zmalloc_get_fragmentation_ratio(size_t rss);
size_t zmalloc_get_rss(void);
size_t zmalloc_get_private_dirty(long pid);
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid);
size_t zmalloc_get_memory_size(void);
void zlibc_free(void *ptr);


zmalloc

void *zmalloc(size_t size) {
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
}


分配内存大小为
size+PREFIX_SIZE
的大小,也就意味着zmalloc实际分配比需要多一些的内存,这一部分用于存储size信息。

当无法分配更多内存的时候由
zmalloc_oom_handler
来处理无法malloc出更多的内存

static void zmalloc_default_oom(size_t size) {
fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
size);
fflush(stderr);
abort();
}
static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;


update_zmalloc_stat_alloc
则是记录到目前为止所使用的内存大小

#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) { \
atomicIncr(used_memory,__n,used_memory_mutex); \
} else { \
used_memory += _n; \
} \
} while(0)


这里有一行很有意思,

if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1));


这一行是保证所分配的内存空间是
sizeof(long)
的整数倍,如果不是,九江这个大小扩充到这个大小。

#ifndef HAVE_MALLOC_SIZE
size_t zmalloc_size(void *ptr) {
void *realptr = (char*)ptr-PREFIX_SIZE;
size_t size = *((size_t*)realptr);
/* Assume at least that all the allocations are padded at sizeof(long) by
* the underlying allocator. */
if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1));
return size+PREFIX_SIZE;
}
#endif


tcmalloc
jemalloc
都提供了
malloc_size
的功能可以获得malloc的size,但是glibc的malloc并没有这种功能,所以
zmalloc
的做法是在头留出
PREFIX_SIZE
大小的地方用于存储大小,它只适用于封装的malloc方法不能方便的得到大小时所用,他会有一个real_ptr指向
PREFIX_SIZE
的地方,返回的确是
PREFIX_SIZE
之后的那段地址,然后你想看长度的时候就往前找
PREFIX_SIZE
的地方就行了。如果有时间我也想看看tcmalloc是怎么实现的。

2. zcalloc

calloc与malloc的区别在于,malloc并不会初始化创建出的内存区域,但是calloc会,malloc的问题在于因并不会初始化导致你很难确定你创建出的这块区域中有什么东西,但calloc的问题也很明显,就是calloc会初始化这块区域也意味着他的效率要低于malloc

void *zcalloc(size_t size) {
void *ptr = calloc(1, 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
}


代码基本与zmalloc相同没啥可看的

3. zrealloc

realloc的功能是先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。

void *zrealloc(void *ptr, size_t size) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
#endif
size_t oldsize;
void *newptr;

if (ptr == NULL) return zmalloc(size);
#ifdef HAVE_MALLOC_SIZE
oldsize = zmalloc_size(ptr);
newptr = realloc(ptr,size);
if (!newptr) zmalloc_oom_handler(size);

update_zmalloc_stat_free(oldsize);
update_zmalloc_stat_alloc(zmalloc_size(newptr));
return newptr;
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
newptr = realloc(realptr,size+PREFIX_SIZE);
if (!newptr) zmalloc_oom_handler(size);

*((size_t*)newptr) = size;
update_zmalloc_stat_free(oldsize);
update_zmalloc_stat_alloc(size);
return (char*)newptr+PREFIX_SIZE;
#endif
}


在zmalloc部分介绍过zmalloc是如何处理并得到
PREFIX_SIZE
的,如不能直接得到size,只能往前找
PREFIX_SIZE
即大小,如无法分配realloc会删除原有的内存并移动到另外一块区域所以需要先用
update_zmalloc_stat_free(oldsize)
来删除之前的记录并用
update_zmalloc_stat_alloc(size)
添加新的。

4. zfree

void zfree(void *ptr) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
size_t oldsize;
#endif

if (ptr == NULL) return;
#ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_free(zmalloc_size(ptr));
free(ptr);
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
free(realptr);
#endif
}


总体来说就是zmalloc的相反的逻辑

5. strdup

strdup实际上就是malloc和memcpy的组合使用,只是这里用的malloc是zmalloc而已,具体哪里会用到,还要接着看以后的代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: