mem_root
mysql层通过mem_root管理内存分配,防止频繁分配和释放小内存。
mem_root的定义见include/my_alloc.h:
typedef struct st_mem_root
{
USED_MEM *free; 空闲链表,分配空间时先检查该链表有没有剩余空间,如果没有,再分配新空间
USED_MEM *used; 已经分配完成的链表空间,该链表中的数据空间几乎没有剩余可用空间
USED_MEM *pre_alloc; 预先分配的块
size_t min_malloc; 如果一个USED_MEM块中可用空间left小于该值,这个块放入used链表中
size_t block_size; 初始块的大小
unsigned int block_num; 块的数目
unsigned int first_block_usage; 空闲链表中第一个块检查的次数,如果超过MAX_BLOCK_USAGE_BEFORE_DROP,该块会被放入used链表中void (*error_handler)(void);
} MEM_ROOT;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;
该结构主要有两个链表: free和used。 每个链表使用USED_MEM作为数据节点,数据节点中存有空间总大小和未使用的空间大小。新建一个数据节点时,先添加到free链表中,随着空间的分配,数据节点中未使用空间大小left小于阈值min_malloc,该数据节点添加到used链表中。分配空间时,先从free链表中检查有没有可用的空间,如果没有,再分配新的块。
相关的函数:
init_alloc_root: 初始化mem_root对象。如果pre_alloc_size不为0,预先创建一个数据块,free和pre_alloc指向这个数据块。
alloc_root:从mem_root对象中分配指定大小length的内存。
该函数先检查free链表的首个数据块,如果该数据块连续ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP(10)次不满足分配条件,并且该块小于ALLOC_MAX_BLOCK_TO_DROP(4K),将该块移动到used链表中。
依次检查free链表中剩余空间大于length的数据块。如果不存在这样的数据块,新建一个数据块。注意新建数据块的大小如下,可以看到分配的空间大小会趋向于越来越大:
block_size= mem_root->block_size * (mem_root->block_num >> 2); 缺省大小,block_num每增加4,分配的内存大小增加block_size
get_size= length+ALIGN_SIZE(sizeof(USED_MEM)); 分配请求的空间大小
get_size= MY_MAX(get_size, block_size); 取最大值
修改该数据块的left剩余空间,如果left剩余空间小于min_malloc,加入到used链表的表头。
reset_root_defaults:
该函数设置block_size和pre_alloc块。如果没有符合要求的pre_alloc块,遍历free链表,查找符合要求的数据块,如果发现完全没有使用的数据块,free该块的空间。如果最后没有找到,新建一个符合要求的块。
free_root: 清空分配的数据块
MY_MARK_BLOCKS_FREE: used链表中的数据库合并到free链表中,并且所有数据块中数据清空(left设置为初始块大小)。最终结果:free链表指向所有的数据块,used链表中空。没有向操作系统释放空间
MY_KEEP_PREALLOC:只保留pre_alloc块,其他块的空间都释放给操作系统。