• Redis内存管理(二)


    上一遍详细的写明了Redis为内存管理所做的初始化工作,这篇文章写具体的函数实现。

    1、zmalloc_size,返回内存池大小函数,因为库不同,所以这个函数在内部有很多的宏定义,通过具体使用的库来确定到底用哪个。

    复制代码
    #define zmalloc_size(p) tc_malloc_size(p)//TCMalloc
    #define zmalloc_size(p) je_malloc_usable_size(p)//Jemalloc
    #define zmalloc_size(p) malloc_size(p)//Mac
    //使用系统库的情况下,要单独写返回函数 #ifndef HAVE_MALLOC_SIZE size_t zmalloc_size(void *ptr)
    { void *realptr = (char*)ptr-PREFIX_SIZE; size_t size = *((size_t*)realptr); //具体的计算还没看懂。 if (size&(sizeof(long)-1))
        size += sizeof(long)-(size&(sizeof(long)-1)); return size+PREFIX_SIZE; } #endif
    复制代码

    2、zmalloc,内存分配函数

    复制代码
    void *zmalloc(size_t size) 
    {/*普通的申请内存大小函数,申请的时候,内存的头部加上一个longlong长度的头部*/
        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
    }
    复制代码

    3.zcalloc

    复制代码
    /*重定义calloc函数,单每次只能申请一块内存,放弃了申请多块内存的功能,实际功能和zmalloc类似*/
    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
    }
    复制代码

    4、zrealloc,该函数放弃了按内存块大小成倍申请的功能,最后功能类似zmalloc

    复制代码
     1 void *zrealloc(void *ptr, size_t size) 
     2 {
     3 #ifndef HAVE_MALLOC_SIZE
     4     //使用成熟库函数的情况下直接修改大小就好。最后更新使用内存量的大小。
     5     void *realptr;
     6 #endif
     7 
     8     size_t oldsize;
     9     void *newptr;
    10 
    11     if (ptr == NULL) return zmalloc(size);
    12 #ifdef HAVE_MALLOC_SIZE
    13     oldsize = zmalloc_size(ptr);
    14     newptr = realloc(ptr,size);
    15     if (!newptr) zmalloc_oom_handler(size);
    16 
    17     update_zmalloc_stat_free(oldsize);
    18     update_zmalloc_stat_alloc(zmalloc_size(newptr));
    19     return newptr;
    20 #else
    21     /*没有使用现成库的情况下,扩展内存
    22     将指针前移,找到真正开始的头部并记录下真正的开始,
    23     按照新的大小重新申请内存,然后释放旧的,最后更新使用内存量的大小。
    24     */
    25     realptr = (char*)ptr-PREFIX_SIZE;
    26     oldsize = *((size_t*)realptr);
    27     newptr = realloc(realptr,size+PREFIX_SIZE);
    28     if (!newptr) zmalloc_oom_handler(size);
    29 
    30     *((size_t*)newptr) = size;
    31     update_zmalloc_stat_free(oldsize);
    32     update_zmalloc_stat_alloc(size);
    33     return (char*)newptr+PREFIX_SIZE;
    34 #endif
    35 }
    复制代码

    5、zfree,内存释放函数

    复制代码
     1 void zfree(void *ptr) {
    //这个也是分两部分,如果使用了成熟库,直接调用释放就好,如果使用系统的,还要找到真的地址开始的头部然后再释放,最后更新内存使用量的大小。 2 #ifndef HAVE_MALLOC_SIZE 3 void *realptr; 4 size_t oldsize; 5 #endif 6 7 if (ptr == NULL) return; 8 #ifdef HAVE_MALLOC_SIZE 9 update_zmalloc_stat_free(zmalloc_size(ptr)); 10 free(ptr); 11 #else 12 realptr = (char*)ptr-PREFIX_SIZE; 13 oldsize = *((size_t*)realptr); 14 update_zmalloc_stat_free(oldsize+PREFIX_SIZE); 15 free(realptr); 16 #endif 17 }
    复制代码

    6、zstrdup

    复制代码
    1 /*字符串复制函数,给一个指定的字符串在堆上分配内存,注意,这个地方只是给一个字符串分配了内存,并没有转换成系统内统一的sds字符串,强行按sds使用会出错。*/
    2 char *zstrdup(const char *s) {
    3     size_t l = strlen(s)+1;
    4     char *p = zmalloc(l);
    5 
    6     memcpy(p,s,l);
    7     return p;
    8 }
    复制代码

    7、zmalloc_used_memory,获取已经使用的内存的大小

    复制代码
     1 size_t zmalloc_used_memory(void) 
     2 {
     3     size_t um;
     4   //如果是线程安全情况下,读取当前使用内存量时要加互斥锁,不然可能会出现并发问题
     5     if (zmalloc_thread_safe) 
     6     {
     7         #if defined(__ATOMIC_RELAXED) || defined(HAVE_ATOMIC)
     8             um = update_zmalloc_stat_add(0);
     9         #else
    10             pthread_mutex_lock(&used_memory_mutex);
    11             um = used_memory;
    12             pthread_mutex_unlock(&used_memory_mutex);
    13         #endif
    14     }
    15     else
    16     {//如果没设定线程安全,直接读取
    17         um = used_memory;
    18     }
    19 
    20     return um;
    21 }
    复制代码

    8、zmalloc_enable_thread_safeness,设置线程安全,多线程模式下,最好是设置了,系统好像没提供解除的函数,也就是说系统一但确定了模式将不能改变。

    void zmalloc_enable_thread_safeness(void) 
    {
        zmalloc_thread_safe = 1;
    }

    9、zmalloc_set_oom_handler,设置内存异常处理函数,如果不设置,系统有一个默认的。

    void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) 
    {
        zmalloc_oom_handler = oom_handler;
    }

    10、zmalloc_get_rss,获取进程总的虚拟内存的大小

    复制代码
    #if defined(HAVE_PROC_STAT)
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    /*计算使用内存的总值*/
    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;
        /*从/proc/PID/stat文件中读取Virtual memory size的大小,指的是虚拟内存的页数*/
        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; /* stat中第24个值记录的是虚拟内存的页数*/
        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;
    }
    #elif defined(HAVE_TASKINFO)
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/sysctl.h>
    #include <mach/task.h>
    #include <mach/mach_init.h>
    
    size_t zmalloc_get_rss(void) {
        task_t task = MACH_PORT_NULL;
        struct task_basic_info t_info;
        mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
    
        if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS)
            return 0;
        task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
    
        return t_info.resident_size;
    }
    #else
    size_t zmalloc_get_rss(void) {
        /* If we can't get the RSS in an OS-specific way for this system just
         * return the memory usage we estimated in zmalloc()..
         *
         * Fragmentation will appear to be always 1 (no fragmentation)
         * of course... */
        return zmalloc_used_memory();
    }
    #endif
    复制代码

    11、zmalloc_get_fragmentation_ratio,计算某块内存占已经使用的内存的百分比。

    float zmalloc_get_fragmentation_ratio(size_t rss) 
    {
        return (float)rss/zmalloc_used_memory();
    }

    12、最后两个函数不知道干嘛用的,所以没分析

    复制代码
    #if defined(HAVE_PROC_SMAPS)
    size_t zmalloc_get_smap_bytes_by_field(char *field) 
    {
        char line[1024];
        size_t bytes = 0;
        FILE *fp = fopen("/proc/self/smaps","r");
        int flen = strlen(field);
    
        if (!fp) return 0;
        while(fgets(line,sizeof(line),fp) != NULL) {
            if (strncmp(line,field,flen) == 0) {
                char *p = strchr(line,'k');
                if (p) {
                    *p = '';
                    bytes += strtol(line+flen,NULL,10) * 1024;
                }
            }
        }
        fclose(fp);
        return bytes;
    }
    #else
    size_t zmalloc_get_smap_bytes_by_field(char *field) 
    {
        ((void) field);
        return 0;
    }
    #endif
    
    size_t zmalloc_get_private_dirty(void) {
        return zmalloc_get_smap_bytes_by_field("Private_Dirty:");
    }
    复制代码

    内存管理模块基本上分析完了,最后两个函数不知道是干嘛的等回头用的时候再说,总的来看,内存管理不是很麻烦,只给内存块加了一个头部来记录快大小,模块也充分考虑了性能,优先使用成熟的内存管理池,如果没有的话就优先使用编译器支持的原子操作,最后再都不支持的情况下才会使用互斥锁,如果在Redis使用的过程中互斥锁降低了性能,可以考虑升级gcc版本,或者安装两个成熟的内存管理库来提高性能。

  • 相关阅读:
    WINDOWS操作系统下OpenERP源码运行的环境:eclipse + pydev + python2.7
    12款优秀的 Twitter Bootstrap 组件和工具
    推荐电脑上好用的但相对冷门的软件
    财务记账演练
    OpenERP Custom Sample Module Development – OpenERP Quick Start Guide
    免费编程入门教程资源推荐搜集
    50个很棒的Python模块
    kindle 资源
    怎么看内存的类型?
    计算机必懂的53个英文单词和缩写
  • 原文地址:https://www.cnblogs.com/likui360/p/5272975.html
Copyright © 2020-2023  润新知