• redis源代码解读之内存管理————zmalloc文件


    本文章主要记录本人在看redis源代码的一些理解和想法。由于功力有限,肯定会出现故障,所以。希望高手给出指正。

    第一篇就是内存相关的介绍。由于我喜欢先看一些组件的东西,再看总体的流程。

    先上一下代码吧

    头文件

    //主要提供内存分配和释放的基础功能

    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(void);
    size_t zmalloc_get_rss(void);
    size_t zmalloc_get_private_dirty(void);
    void zlibc_free(void *ptr);
    
    #ifndef HAVE_MALLOC_SIZE
    size_t zmalloc_size(void *ptr);
    #endif

    0.前言

    在这块代码我们能够看到HAVE_ATOMIC 宏定义,有什么作用呢。

    该文件主要提供了tcmalloc 和jemalloc内存的管理。

    tcmalloc是google perftool的一部分。与一般的内存池不同,它直接与os打交道,内存闲置时os会进行回收(stl内存池就不回收),同一时候使用TLS(Thread local storage)管理内存池,避免一个线程内分配内存都要同步。


    jemalloc与tcmalloc相似,作者Jason Evans是Free BSD开发者,性能与使用率与tcmalloc不相伯仲。

    tcmalloc更方便与google perftool集成,进行性能评測。



    zmalloc主要是 提供了对malloc函数的封装,假设是glibc的malloc函数,那么分配的内存是长度+要分配的的内存。然后将头部放入大小

    1.申请内存

    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
    }
    

     update_zmalloc_stat_alloc(zmalloc_size(ptr));是先将自己内存对齐,假设long是4位就对齐到4的整数倍。然后将内存的大小记录下来到一个全局变量中
    例如以下代码
    #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)
    内存对其有非常多种方式,他这样的也是非常有意思。

    使用long就能够无论是64机器还是32位机器了。假设是在多线程情况下是使用了相互排斥锁。


    假设没定义宏HAVE_MALLOC_SIZE,就在内存的前边四个字节存储器大小。最后返回内存的起始地址。

    同理
    void *zcalloc(size_t size)
    该方法封装了calloc 函数功能。

    2.又一次申请内存

    代码例如以下
    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
    }
    

    该方法就是对指定的一段内存又一次申请指定大小的内存。地址没变化。



    3.释放内存

    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
    }

    4.拷贝内存,封装了memcpy函数
    char *zstrdup(const char *s) {
        size_t l = strlen(s)+1;
        char *p = zmalloc(l);
    
        memcpy(p,s,l);
        return p;
    }
    

    5获取RSS 物理内存值
    通过查看进程的/proc/pid/stat 文件的第23行。

    size_t zmalloc_get_rss(void) {
        int page = sysconf(_SC_PAGESIZE);
        size_t rss;
        char buf[4096];
        char filename[256];
        int fd, count;
        char *p, *x;
    
        snprintf(filename,256,"/proc/%d/stat",getpid());
        if ((fd = open(filename,O_RDONLY)) == -1) return 0;
        if (read(fd,buf,4096) <= 0) {
            close(fd);
            return 0;
        }
        close(fd);
    
        p = buf;
        count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
        while(p && count--) {
            p = strchr(p,' ');
            if (p) p++;
        }
        if (!p) return 0;
        x = strchr(p,' ');
        if (!x) return 0;
        *x = '';
    
        rss = strtoll(p,NULL,10);
        rss *= page;
        return rss;
    }

    6.内存使用率

    /* Fragmentation = RSS / allocated-bytes */
    float zmalloc_get_fragmentation_ratio(void) {
        return (float)zmalloc_get_rss()/zmalloc_used_memory();
    }

    7.多线程下的原子添加


    #ifdef HAVE_ATOMIC
    #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
    #define update_zmalloc_stat_add(__n) do { 
        pthread_mutex_lock(&used_memory_mutex); 
        used_memory += (__n); 
        pthread_mutex_unlock(&used_memory_mutex); 
    } while(0)
    
    #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
    


    redis这块内存使用谷歌和jemalloc 进行的内存管理是很高效的。

    对外提供的接口也比較简洁。

    值得学习。



    很多其它文章。欢迎关注:




  • 相关阅读:
    md5加密(4)
    生成短的uuid
    九九乘法
    闰年判断
    初识网络传输
    省选模拟77
    省选模拟76
    省选模拟75
    省选模拟74
    省选模拟73
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5103587.html
Copyright © 2020-2023  润新知