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而已,具体哪里会用到,还要接着看以后的代码
相关文章推荐
- Redis源码阅读笔记(1)-- 动态字符串sds
- Redis源码阅读笔记—sds
- Redis源码阅读笔记(2)-- 对象robj
- redis源码阅读笔记(一):sds
- Redis源码阅读笔记-跳跃表结构
- Redis源码阅读笔记--六大数据结构和五大对象
- Redis源码阅读笔记(2)——字典(Map)实现原理
- Redis源码阅读笔记—adlist
- Redis源码阅读笔记-动态字符串(SDS)结构
- Redis源码阅读笔记-链表结构
- Redis源码阅读笔记—sds
- redis源码阅读笔记-- 事件
- Redis源码阅读笔记(3)-- 字典dict
- redis 源码阅读笔记
- Redis源码阅读笔记(1)——简单动态字符串sds实现原理
- Redis源码阅读笔记-压缩列表结构
- 基于mfc的pc客户端源码阅读笔记
- gtk学习笔记 - vim + ctags 阅读源码
- FastDFS源码阅读笔记(二)
- Boa 源码阅读笔记