当前位置:   article > 正文

mysql my malloc_Mysql源码学习:内存管理模块MEM_ROOT

mysql my malloc_Mysql源码学习:内存管理模块MEM_ROOT

MEM_ROOT为mysql的内存管理模块,用于统一申请和释放内存,减少在堆中的内存申请操作的次数,以提升性能。

基础结构

申请的内存空间使用的结构体

typedef struct st_used_mem

{

struct st_used_mem *next; /*连接指针,连接当前链表下所有的结构体

unsigned int left; /*当前结构体中剩余的空间

unsigned int size; /*结构体申请的size

}USED_MEM;

MEM_ROOT结构体

typedef struct st_mem_root

{

USED_MEM *free;/*可以使用的预申请空间链表

USED_MEM *used;/*使用中,且没有可用空间的预申请空间链表

USED_MEM *pre_alloc; /*初始时预申请的

size_t min_malloc /*剩余的最小空间,如果预申请的空间剩余大小小于min_alloc,将他移到used队列

size_t block_size; /*每次申请内存块的基础大小

unsigned int block_num; /* 计算申请内存的参数,最终每次申请内存块大小为 block_size * (block_num >> 2) ,每次申请内存后该值+1

unsigned int first_block_usage; /*记录第一块内存块使用的次数,用作是否将第一块移入used链表的判断依据之一

size_t max_capacity; /*允许申请的最大空间地址的大小

size_t allocated_size; /*总计分配内存大小

void (*error_handler)(void); /*操作出错的是错误处理函数

}MEM_ROOT;

一个使用中的MEM_ROOT结构如下:

其中每个内存块由一个USED_MEM结构体和内存空间组成,内存空间为向操作系统申请的内存。

MEM_ROOT结构体中free链表和used链表将所有的内存块连接起来(红色斜线卫为已经使用的内存)。

初始化

初始化过程主要为所有变量赋初始值。如果输入参数pre_alloc_size为0,那么初始化过程是不申请任何大小的内存空间的。

void init_alloc_root(...MEM_ROOT *mem_root, size_t block_size,size_t pre_alloc_size )

| mem_root->pre_alloc= 0;

| ...

/*每次申请空间的大小,用于后续申请空间使用

| mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;

/*如果指定了参数pre_alloc_size,那么申请一块大小为pre_alloc_size的内存空间

| if (pre_alloc_size)

| if ((mem_root->free= mem_root->pre_alloc = (USED_MEM*) my_malloc(,..pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM)),

申请空间

mysql会首先使用my_malloc根据初始化时输入的空间大小预申请一块较大的空间,每次调用alloc_root函数,会在这块大的空间中,分配出一块空间地址作为alloc_root函数的返回(目的是将多次零散的malloc操作合并成一次大的malloc操作,以提升性能)。

当预申请的空间不足时。会重新申请一块大的地址空间。当alloc_root需要申请的空间大于预申请的空间时,mem_root会动态的调整预申请空间的大小来满足alloc_root的需求,如果max_capacity未设置,那么无论alloc_root申请多大的空间,mem_root均会申请足够大的预申请空间来满足它的需求(如果操作系统允许)。

void *alloc_root(MEM_ROOT *mem_root, size_t length)

/*

* 1.读取第一个内存空间节点,并判断是否应该将第一个空间节点放入used链表

* 2.在整个free链表中查看,查找第一个满足需求的(剩余空间超过length)的节点

| (*(prev= &mem_root->free)) /*在free链表中读取第一个节点

| if ((*prev)->left < length) && /*该节点剩余空间不满足需求

mem_root->first_block_usage++ >= ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP && /*该节点的使用次数达到了权值(10次)

(*prev)->left < ALLOC_MAX_BLOCK_TO_DROP /*该节点剩余空间小于4096

| next->next= mem_root->used; /*同时满足上述3个条件,将节点移入used链表

| mem_root->used= next;

| mem_root->first_block_usage= 0;

| for (next= *prev ; next && next->left < length ; next= next->next) /*遍历free链表,查找满足需求

| prev= &next->next;

/*

* 在free链表中没有找到满足需要的内存块节点,或者第一次使用mem_root,需要申请新的内存节点

| if (! next) /*没找到满足条件的节点

| block_size= mem_root->block_size * (mem_root->block_num >> 2); /*mem_root 需要申请的大小

| get_size= length+ALIGN_SIZE(sizeof(USED_MEM)); /*需求的空间大小

| get_size= MY_MAX(get_size, block_size); /*两者取大最为申请值

| is_mem_available(mem_root, get_size) /*判断是否超出了允许的最大值

| if (mem_root->max_capacity

if ((mem_root->allocated_size + size) > mem_root->max_capacity)

| next = (USED_MEM*) my_malloc..., get_size,MYF(MY_WME | ME_FATALERROR) /*申请 get_size大小的空间

/*

* 更新mem_root参数,根据新的内存块的剩余空间的大小,将内存块放入free链表或者used链表

| mem_root->allocated_size+= get_size;

| mem_root->block_num++;

| next->next= *prev; /*放入free链表

| ...

| if ((next->left-= (uint)length) < mem_root->min_malloc) /*如果剩余空间小于min_malloc,放入used链表

next->next= mem_root->used;

重置

复用MEM_ROOT,重置后,所有的内存块均被重置为未使用状态,所有的内存块均在free链表中。与全新的MEM_ROOT相比,重置后的mem_root并不会释放已经申请好的内存块。

mark_blocks_free(MEM_ROOT* root)

|last= &root->free;

|for (next= root->free; next; next= *(last= &next->next)) /*遍历free 链表,将所有内存块的left置位最大

next->left= next->size - (uint)ALIGN_SIZE(sizeof(USED_MEM));

|*last= next=root->used; /*将used链表接在free链表最后

| for (; next; next= next->next) /*遍历原used链表,将所有内存块的left置位最大

next->left= next->size - (uint)ALIGN_SIZE(sizeof(USED_MEM));

| root->used= 0;

| root->first_block_usage= 0;

释放

根据输入的参数,或者将MEM_ROOT重置,或者将所有申请的内存块释放。

void free_root(MEM_ROOT *root, myf MyFlags)

|if (MyFlags & MY_MARK_BLOCKS_FREE) /*参数为MY_MARK_BLOCKS_FREE,重置MEM_ROOT*/

mark_blocks_free(root);

/*

* 遍历free链表和used链表,将除pre_alloc外的所有内存块释放,重置pre_alloc

*/

| for (next=root->used; next ;)

my_free(old);

| for (next=root->free; next ;)

my_free(old);

| if (root->pre_alloc)

root->free=root->pre_alloc;

总结

MEM_ROOT模块为mysql内部的内存管理模块,使用MEM_ROOT模块,每次申请一个较大的内存块,将这个内存块根据需求拆分为小内存块使用。

对于申请内存size较少,并且申请较为频繁的情况,MEM_ROOT模块能比较好的提升性能。

而对于申请内存size较大的情况。使用MEM_ROOT与直接使用malloc申请,性能几乎持平。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/969397
推荐阅读
相关标签
  

闽ICP备14008679号