Redis 3.0源码分析-内存分配zmalloc

Redis默认的内存分配器采用jemalloc,可选的分配器还有:glibc、tcmalloc。内存分配器为了更好地管理和重复利用内存,分配内存策略一般采用固定范围的内存块进行分配。例如jemalloc在64位系统中将内存空间划分为:小、大、巨大三个范围。每个范围内又划分为多个小的内存单元,如下所示:

  • 小:[8 byte],[16 byte, 32 byte, 48 byte, …, 128 byte], [192 byte, 256 byte, …, 512 btye], [768 byte, 1024 byte, …, 3849 byte]
  • 大:[4KB, 8KB, 12KB, …, 4072KB]
  • 巨大:[4MB, 8MB, 12MB, …]

zmalloc.h分析:

源文件zmalloc.h的宏定义显示了对内存分配器的选择:
在这里插入图片描述
我们可以看到对USE_TCMALLOC的宏定义,其中__xstr是宏值字符串化。如果定义了USE_TCMALLOC,也就是使用TCMALLOC,则定义ZMALLOC_LIB为tcmalloc-主版本号.次版本号,并且引入google/tcmalloc.h文件。如果版本是在次版本大于1.6或主版本号大于1的情况下,定义HAVE_MALLOC_SIZE为1,并定义zmalloc_size§为tc_malloc_size§。否则会输出需要更新的版本。
HAVE_MALLOC_SIZE宏定义是对zmalloc.c提供函数是否有zmalloc_size函数的控制。
在这里插入图片描述
可以看到对USE_JEMALLOC的宏定义。如果定义了USE_JEMALLOC,也就是使用JEMALLOC,则定义JEMALLOC_LIB为jemalloc-主版本号.次版本号,并且引入jemalloc/jemalloc.h文件。如果版本是在次版本大于2.1或主版本号大于2的情况下,定义HAVE_MALLOC_SIZE为1,并定义zmalloc_size§为je_malloc_usable_size§。否则会输出需要更新的版本。
在这里插入图片描述
如果jemalloc和tcmalloc都没有使用就用glibc的库函数
在这里插入图片描述

zmalloc.h包含的函数列表:

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


float zmalloc_get_fragmentation_ratio(size_t rss);
size_t zmalloc_get_rss(void);
size_t zmalloc_get_private_dirty(void);

void zlibc_free(void *ptr);

zmalloc.c分析:

这是在源文件中对函数调用进行替换,以保证zmalloc是对内存函数的包装。
在这里插入图片描述

zmalloc针对used_memory的多线程保护的处理:used_memory是使用的内存量,而used_memory_mutex是针对used_memory变量的保护锁。zmalloc_thread_safe是对zmalloc线程安全性的开关。
在这里插入图片描述
当使用原子操作时,就使用__sync_add_and_fetch和__sync_sub_and_fetch。否则就使用线程锁,update_zmalloc_stat_add和update_zmalloc_stat_sub就是提供的对加锁解锁操作的封装。
在这里插入图片描述
如果zmalloc_thread_safe开启就使用线程安全的操作,如果不开启就是一般操作。而90行的代码是确保内存对齐,就是保证对齐边缘是long的大小。
在这里插入图片描述

对函数的简要分析:如果无法得到需要的内存,将会调用zmalloc_oom_handler函数,该函数将打印出错信息。
在这里插入图片描述
通过HAVE_MALLOC_SIZE来决定是否提供zmalloc_size函数,如果没有提供,那么在其他函数实现时,需要写提取内存大小的代码。内存的大小是存放在分配的内存的首地址中。
在这里插入图片描述
在源文件中对HAVE_MALLOC_SIZE的处理:如果定义了HAVE_MALLOC_SIZE宏定义,则PREFIX_SIZE定义为0。否则根据处理器架构来定义PREFIX_SIZE。
在这里插入图片描述

源文件中还有一部分比较重要的函数是获取RSS信息
这部分函数有多种实现方式:提取/proc中的信息、taskinfo函数和zmalloc实现的统计方法zmalloc_used_memory()

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 = '\0';

    rss = strtoll(p,NULL,10);
    rss *= page;
    return rss;
}

这是从/proc文件系统中提取内存rss,使用页面数乘页大小。
在这里插入图片描述
task_info函数
在这里插入图片描述
zmalloc实现的统计方法
在这里插入图片描述

zmalloc_get_private_dirty的实现:使用/poc/self/smaps的统计信息
在这里插入图片描述

关于zmalloc的参考
https://blog.csdn.net/terence1212/article/details/53542034

关于tcmalloc的参考
https://yq.aliyun.com/articles/6045
https://zhuanlan.zhihu.com/p/29216091

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值