Redis源码zmalloc内存管理(2)

9 篇文章 0 订阅
7 篇文章 1 订阅

zmalloc内存管理(2)(Redis源码学习)

1. 背景介绍

在上一篇zmalloc内存管理(1)中留了一个问题,在 zmalloc 函数中用出现了条件编译,涉及到是否定义 HAVE_MALLOC_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

如果你所使用的环境编译redis时,根据所在Linux编译环境会检测到是否定义 HAVE_MALLOC_SIZE,然后决定执行时走哪一部分代码。
下面就 HAVE_MALLOC_SIZE 进行相关介绍。

2. 编译选项

下面是redis对应src目录下面的Makefile文件中的部分片段,进行说明

uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')

# Default allocator
ifeq ($(uname_S),Linux)
	MALLOC=jemalloc
else
	MALLOC=libc
endif

# Backwards compatibility for selecting an allocator
ifeq ($(USE_TCMALLOC),yes)
	MALLOC=tcmalloc
endif

ifeq ($(USE_TCMALLOC_MINIMAL),yes)
	MALLOC=tcmalloc_minimal
endif

ifeq ($(USE_JEMALLOC),yes)
	MALLOC=jemalloc
endif

uname_S 那一句就是执行sh命令,在RHEL6.9中然后得出变量uname_S值为Linux,可以将改行括号里的命令直接拷贝到Linux命令行进行执行,就能看到结果。

接下来是条件判断语句,如果 uname_S 值等于 Linux,则将变量 MALLOC 赋值为 jemalloc(shell脚本中的变量是直接使用的),反之则为libc。

说明:

我在编译Redis-3.0 时,直接make报错失败,所以当时就按照 README 文件中的命令 make MALLOC=libc 能编译通过。文中说 jemalloc 是Linux系统中默认的内存分配方式,因为 jemalloc 进行内存操作,产生的内存碎片比较少。学习Redis源码开始时,主要是看内部实现细节,可以先以源码为主,屡清大概流程,至于内测分配方式,等对Redis源码阅读的差不多,架构等环节清楚后,再来研究这些细节。

后面的 ifeq 条件判断,都是针对不同的内存分配方式,来给予 MALLOC 不同的值。

3. zmalloc内存分配方式

上面2中的makefile中的相关变量值,会影响到后续zmalloc的源码条件编译选项,这样就能屏蔽Redis不同运行平台的差异,方便不同平台复用相同的函数。

HAVE_MALLOC_SIZE相关定义在 zmalloc.h 文件中,具体内容如下:

#if defined(USE_TCMALLOC)
#define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))
#include <google/tcmalloc.h>
#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) tc_malloc_size(p)
#else
#error "Newer version of tcmalloc required"
#endif

#elif defined(USE_JEMALLOC)
#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))
#include <jemalloc/jemalloc.h>
#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) je_malloc_usable_size(p)
#else
#error "Newer version of jemalloc required"
#endif

#elif defined(__APPLE__)
#include <malloc/malloc.h>
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) malloc_size(p)
#endif

#ifndef ZMALLOC_LIB
#define ZMALLOC_LIB "libc"
#endif 
...
#ifndef HAVE_MALLOC_SIZE
size_t zmalloc_size(void *ptr);
#endif

针对上面这段宏的预处理,可以得知:

(1). 若系统存在 TCMALLOC,那就定义 ZMALLOC_LIB,并包含相应的头文件,内存相关操作函数就直接使用TCMALLOC。
(2). 若系统存在 JEMALLOC,,那就定义 JEMALLOC_LIB,并包含相应的头文件,内存相关操作函数就直接使用JEMALLOC。
(3). 若系统是苹果,则直接使用 malloc/malloc.h 中的相关函数操作内存。
(4). 其它情况,在每一段分配好的空间前头,同时多分配一个定长的字段,用来记录分配的空间大小。 

tc_malloc是google开源处理的一套内存管理库,是用C++实现的,jemalloc 也是一个内存创管理库,jemalloc聚集了malloc的使用过程中所验证的很多技术。

可能你有疑问系统不是有了malloc 吗,为什么还有这样的内存管理库?? 由于经典的libc的分配器碎片率为较高,下面第二链接的文章对比了上面几个内存分配方式的碎片率对比。
文中大概结论:采用tcmalloc时碎片率是最低的,为1.01,jemalloc为1.02,而libc的分配器碎片率为1.31,仅供参考,如有兴趣,可以自行验证。

目前是学习Redis源码阶段,可以先不用关心内存分配方式,先能成功编译安装Redis,调试源码,学习验证代码是最重要的,等后续可以再验证不同内存分配方式下Redis的表现。

另外针对malloc和free,有一定的缺陷,主要记录两点:

(1). 频繁调用malloc,free会造成大量内存碎片,无法回收重新利用,造成内存使用率低。
(2). 作为系统调用,其系统开销远远大于一般函数。

本人才疏学浅,理解水平有限,如果有错误及不当之处,请批评指正。

如果对您有一点点帮助,请帮忙转发和关注工作号:hongmaolinux,感激不尽_!

参考链接:
https://blog.csdn.net/daniel_ustc/article/details/26478995

https://www.jb51.net/article/100575.htm

https://blog.csdn.net/jisuanji198509/article/details/115393116

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值