redis 内存管理zmalloc

来源:互联网 发布:人民日报图文数据库 编辑:程序博客网 时间:2024/06/07 14:00


          

             redis封装的内部结构,脑子里要有这幅图,就差不多了。实际分配的内存比size要多。



redis的zmalloc函数

// 已经使用的内存,malloc函数增加,free减少这个值static size_t used_memory = 0;// 线程安全,其实没用;redis是单线程模型static int zmalloc_thread_safe = 0;// used_memory变量作为临界区,做同步pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;// 默认内存溢出handlerstatic void zmalloc_default_oom(size_t size) {// 打印日志    fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",        size);// 还是打印日志,到哪里 TODO    fflush(stderr);// TODO    abort();}static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;void *zmalloc(size_t size) {    void *ptr = malloc(size+PREFIX_SIZE);// 如果申请失败,一定是OOM    if (!ptr) zmalloc_oom_handler(size);#ifdef HAVE_MALLOC_SIZE// 统计用计数器增增加    update_zmalloc_stat_alloc(zmalloc_size(ptr));    return ptr;#else// 使用4个字节记录申请内存字节数    *((size_t*)ptr) = size;    update_zmalloc_stat_alloc(size+PREFIX_SIZE);// 返回实际内存指针    return (char*)ptr+PREFIX_SIZE;#endif
       1 首先把和多线程相关的去掉,先忽略。

       2 HAVE_MALLOC_SIZE是什么意思zmalloc_size 内容是什么?

http://www.petermao.com/redis/78.html 这篇文章 对 HAVE_MALLOC_SIZE 有一行注释,“另外对于 apple系统,可以用malloc_size(redis_malloc_size是对它的封装)取得指针所指向的内存块大小,因此就不需要手动保存大小了。” 据此明了,redis 实现了类似apple的功能,使用申请的内存的前一段PREFIX_SIZE,保存真正申请(可以使用)的内存字节数。

        zmalloc_size函数的注释,对于一些不支持zmalloc_size的系统这种情况,redis在每次分配内存的时候使用前几个字节保存实际使用的字节数。

/* Provide zmalloc_size() for systems where this function is not provided by * malloc itself, given that in that case we store a header with this * information as the first bytes of every allocation. */#ifndef HAVE_MALLOC_SIZEsize_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

    3 PREFIX_SIZE 记录一次分配内存,使用内存的字节数,他的大小怎么定义的?

http://blog.csdn.net/jinjinstudy/article/details/18189567

        PREFIX_SIZE用来记录malloc已分配到的内存大小  
        tc_malloc/je_malloc/Mac平台分别采用tc_malloc_size/malloc_size/je_malloc_usable_size(不需要单独开空间计算得到的内存大小,PREFIX_SIZE值置为0)  
        linux和sun平台分别采用sizeof(size_t)=8字节和sizeof(long long)定长字段记录,所以要记录分配空间的大小  

#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
       

       简单来讲,可以把PREFIX_SIZE看作一个size_t的大小(一般是8字节)。它的主要作用是来记录本次分配的内存空间大小。对于不同平台来说,PREFIX_SIZE的值是不同的。

  • 如果平台内存分配策略,已经使用前几个字节记录大小(HAVE_MALLOC_SIZE),PREFIX_SIZE = 0就不用再额外申请内存。
  • 如果是sun的服务器,则分配一个8个字节当前缀 sizeof(long long)。
  • 其他的使用size_t的大小8个字节。
     4  zmalloc_size函数字节填充pading。翻译下注释:每次内存分配都是8字节的整数倍,如果不够进pading凑足8的整数倍。

    /* 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));
     例如 size = 15 ,二进制表示 11111,sizeof(long) - 1 = 7,二进制表示111, 按位与15 & 7,结果是111,不等于0;所以,size +=

8 - 7,结果是16.

     5 当使用tcmalloc库/jemalloc库的时候,显式覆盖malloc/calloc/realloc/free的方法  

/* Explicitly override malloc/free etc when using tcmalloc. */#if defined(USE_TCMALLOC)#define malloc(size) tc_malloc(size)#define calloc(count,size) tc_calloc(count,size)#define realloc(ptr,size) tc_realloc(ptr,size)#define free(ptr) tc_free(ptr)#elif defined(USE_JEMALLOC)#define malloc(size) je_malloc(size)#define calloc(count,size) je_calloc(count,size)#define realloc(ptr,size) je_realloc(ptr,size)#define free(ptr) je_free(ptr)#endif
    6 增加内存和释放内存时used_memory计数器

#if defined(__ATOMIC_RELAXED)// 平台相关define#define update_zmalloc_stat_add(__n) __atomic_add_fetch(&used_memory, (__n), __ATOMIC_RELAXED)#define update_zmalloc_stat_sub(__n) __atomic_sub_fetch(&used_memory, (__n), __ATOMIC_RELAXED)#elif defined(HAVE_ATOMIC)// 平台相关define#define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n))#define update_zmalloc_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n))#else// 已使用内存 增加delta#define update_zmalloc_stat_add(__n) do { \    pthread_mutex_lock(&used_memory_mutex); \    used_memory += (__n); \    pthread_mutex_unlock(&used_memory_mutex); \} while(0)// 只执行一次// 已使用内存 减少delta#define update_zmalloc_stat_sub(__n) do { \    pthread_mutex_lock(&used_memory_mutex); \    used_memory -= (__n); \    pthread_mutex_unlock(&used_memory_mutex); \} while(0)#endif#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) { \// 多线程case使用        update_zmalloc_stat_add(_n); \    } else { \        used_memory += _n; \    } \} while(0)#define update_zmalloc_stat_free(__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_sub(_n); \    } else { \        used_memory -= _n; \    } \} while(0)

     7 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}

     http://blog.csdn.net/firecityplans/article/details/4490124/

     void *malloc(unsigned size)//动态申请size个字节的内存空间;功能:在内存的动态存储区中分配一块长度为" size" 字节的连续区域。函数的返回值为该区域的首地址。。(类型说明符*)表示把返回值强制转换为该类型指针。

     (void *)calloc(unsigned n,unsigned size)//      用于向系统动态申请n个, 每个占size个字节的内存空间; 并把分配的内存全都初始化为零值。函数的返回值为该区域的首地址

     (void *)realloc(void *p,unsigned size)//将指针p所指向的已分配内存区的大小改为size

区别:两者都是动态分配内存。主要的不同是malloc不初始化分配的内存,已分配的内存中可以是任意的值. calloc 初始化已分配的内存为0。次要的不同是calloc返回的是一个数组,而malloc返回的是一个对象。


      8  zrealloc 函数。realloc的功能是先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
/**   重新分配内存;外部传入的指针地址是字符串地址;*/void *zrealloc(void *ptr, size_t size) {#ifndef HAVE_MALLOC_SIZE    // ptr - PREFIX_SIZE 的地址    void *realptr;#endif    size_t oldsize;    void *newptr;// 如果ptr == null,重新分配一段内存    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}

          补说明,realloc函数的说明

realloc(void *__ptr, size_t __size):更改已经配置的内存空间,即更改由malloc()函数分配的内存空间的大小。


如果将分配的内存减少,realloc仅仅是改变索引的信息。


如果是将分配的内存扩大,则有以下情况:
1)如果当前内存段后面有需要的内存空间,则直接扩展这段内存空间,realloc()将返回原指针。
2)如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存块位置。
3)如果申请失败,将返回NULL,此时,原来的指针仍然有效。

注意:如果调用成功,不管当前内存段后面的空闲空间是否满足要求,都会释放掉原来的指针,重新返回一个指针,虽然返回的指针有可能和原来的指针一样,即不能再次释放掉原来的指针。


       9 free函数,系统中除了分配请求大小的内存外,还在该内存块头部保存了该内存块的大小,这样,释放的时候可以通过该大小找到该内存块的起始位置:

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}

         10 函数 获取已使用内存量,有并发的场景,前后加锁。

size_t zmalloc_used_memory(void) {    size_t um;    if (zmalloc_thread_safe) {#if defined(__ATOMIC_RELAXED) || defined(HAVE_ATOMIC)        um = update_zmalloc_stat_add(0);#else        pthread_mutex_lock(&used_memory_mutex);        um = used_memory;        pthread_mutex_unlock(&used_memory_mutex);#endif    }    else {        um = used_memory;    }    return um;}

参考:

   http://blog.csdn.net/guodongxiaren/article/details/44747719

   http://blog.csdn.net/guodongxiaren/article/details/44783767