赞
踩
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申请,性能几乎持平。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。