Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1744077
  • 博文数量: 107
  • 博客积分: 1715
  • 博客等级: 上尉
  • 技术积分: 3168
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-18 18:42
个人简介

阿里巴巴DBA,原去哪儿网DBA。专注于MySQL源码研究、DBA运维、CGroup虚拟化及Linux Kernel源码研究等。 github:https://github.com/HengWang/ Email:king_wangheng@163.com 微博 :@王恒-Henry QQ :506437736

文章分类

全部博文(107)

文章存档

2014年(2)

2013年(38)

2012年(67)

分类: Mysql/postgreSQL

2012-11-17 19:27:40

目的

       MySQL源码中,MEM_ROOT数据结构广泛应用在各个子系统和处理过程中,几乎无处不在。MEM_ROOT数据结构及相关处理方法,主要用于维护一些分配的内存空间,提高分配相同大小和类型的内存空间的效率。

数据结构

       MEM_ROOT的数据结构定义在mysql源码的/include/my_alloc.h文件中。其中,MEM_ROOT中使用了一个重要数据结构:USED_MEM,该结构是已分配的内存块对象。

 

typedef struct st_used_mem

{   /* struct for once_alloc (block) */

  struct st_used_mem *next;    /* Next block in use */

  unsigned int       left;           /* memory left in block  */

  unsigned int       size;           /* size of block */

} USED_MEM;

 

typedef struct st_mem_root

{

  USED_MEM *free;                  /* blocks with free memory in it */

  USED_MEM *used;                  /* blocks almost without free memory */

  USED_MEM *pre_alloc;             /* preallocated block */

  /* if block have less memory it will be put in 'used' list */

  size_t min_malloc;

  size_t block_size;               /* initial block size */

  unsigned int block_num;          /* allocated blocks counter */

  /*

     first free block in queue test counter (if it exceed

     MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list)

  */

  unsigned int first_block_usage;

  void (*error_handler)(void);

} MEM_ROOT;

 

       首先,分析一下USED_MEM数据结构,该结构包含三个参数:next表示下一个USED_MEM对象,是一个链式结构;left表示当前内存块中剩余的内存空间;size表示内存块分配的大小。通过USED_MEM数据结构,就可以将分配的内存块连接成一个链表,供内存分配使用。

       MEM_ROOT数据结构定义了三个USED_MEM指针,其中free表示有空闲内存的内存块;used表示几乎没有空闲内存块,之所以是几乎没有,是因为当first_block_usage大于10,并且内存剩余的内存大小left小于4096时,free列表中的内存块就会被插入used列表;pre_alloc表示预分配的内存块列表。此外,MEM_ROOT定义了最小分配的内存大小min_malloc;初始化内存块的大小block_size;内存块数block_num;之前提到的first_block_usage参数,用于判断free内存块测试次数;以及错误处理函数指针,用于处理内存分配出错时的处理过程。

源码实现

       源码实现核心处理函数包括MEM_ROOT的初始化函数init_alloc_root(),内存分配函数alloc_root(),内存释放函数free_root()。具体的实现代码在mysql源码的/mysys/my_alloc.c文件中,处理代码较多,不再赘述。以下内容,仅对源码的逻辑进行分析。

init_alloc_root()函数

       init_alloc_root()初始化函数,主要对MEM_ROOT的参数进行初始化,默认设置参数min_mallloc的值为32,参数block_num的值为4,参数block_size的值为输入参数block_size的大小减去常量值ALLOC_ROOT_MIN_BLOCK_SIZE(该常量包含USED_MEM的大小、常量MALLOC_OVERHEAD值为8,以及最小block的大小8)。以下是pre_alloc_size0init_alloc_root()函数的初始化状态。

 

1 pre_alloc_size = 0

 

特别注意的,如果输入参数pre_alloc_size的值不为0,那么预分配内存空间,并将pre_mallocfree指针指向该内存空间。pre_alloc_size是需要的内存大小,而实际分配的内存空间大小为pre_alloc_size + ALIGN_SIZE(sizeof(USED_MEM)),因此free指针中的参数size的值为内存实际分配的大小,参数left的值为pre_alloc_size的值。pre_alloc_size不为0情况下,init_alloc_root()函数的初始化状态。

 

 

2 pre_alloc_size不为0

 

alloc_root()函数

       alloc_root()函数式MEM_ROOT最核心的函数,用于内存的分配。该函数的处理逻辑为:首先查看free内存列表中,是否有符合分配长度的block块。如果不存在,那么重新申请内存空间,生成一个新的block块。否则,从找到的block块上分配内存空间,并返回当前block剩余的首地址,并修改left值。如果分配后,该block块的值小于参数min_malloc的值,那么将该block添加到used内存列表中。特别注意的是,在查看free内存列表时,如果free中第一个blockfirst_block_usage值大于10次(即第一个block块查找分配失败次数大于10次),并且left的空间小于4096时,将该block添加到used列表中。

       具体处理逻辑的简化流程图如下所示:

 

3 alloc_root()流程图

 

free_root()函数

       free_root()函数主要用于释放分配的内存空间。但是该函数根据输入不同的标志,用于释放内存的方式有些不同。该函数的处理逻辑为:如果输入的标志仅仅用于标示该内存空间为释放,而不是真正的释放内存,那么调用mark_blocks_free()函数标记所有的空间已经释放,以便于重用。否则,如果输入的标志不需要保留预分配的内存空间,那么预分配空间置为空,并分别释放usedfree列表中的block内存列表。

       具体处理逻辑的简化流程图如图4所示:

 

4 free_root()流程图

 

       其中mark_blocks_free()函数的处理逻辑较为简单,仅仅是将free列表中的每个blockleft值设置为block块的实际可用的内存大小,并将used列表中的block添加到free列表中,执行相同的操作。而free过程在流程图中用函数free代替,该处理代码是对链表的处理,需要特别注意的是,在释放之前需要判断是否是pre_alloc的内存空间。如果是pre_alloc指向的内存空间,不释放该block的内存空间。

结论

       通过以上分析MEM_ROOT数据结构及相关处理操作。可以发现,这种内存分配管理方式,在free_root()时,将内存空间标记为释放,可以使得内存空间重用,提高重复使用相同大小或类型的内存空间的效率,而降低真正内存分配的时间代价。

 

阅读(4951) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~