MySQL 源码解读 -- server层内存管理

server层内存管理

数据结构

image.png

1
2
3
4
struct Block {
Block *prev{nullptr}; /** Previous block; used for freeing. */
};
Block 是MEM_ROOT的子类

Block 的prev 指的都是历史Block, 已经分配完内存的Block
m_max_capacity 为0,表示不限制
m_allocated_size表示的使用的block 合起来的size

Block 内部结构
image.png
要求my_memory_head小于32个字节
image.png

内存申请流程


申请block内部

其中“设置当前block” ,

1
2
3
4
5
6
7
8
9
char *new_mem =
pointer_cast<char *>(new_block) + ALIGN_SIZE(sizeof(*new_block));

new_block->prev = m_current_block;
m_current_block = new_block;


m_current_free_start = new_mem + length;
m_current_free_end = new_mem + new_block_size;

插入现有blockchain

1
2
new_block->prev = m_current_block->prev;
m_current_block->prev = new_block;

其中有个细节, 当申请big内存时, 会将blocksize 增加1/2

这个里面有一点内存浪费逻辑
当内存不够时,并且新申请的内存小于blocksize时, 当前block会变成历史block, 历史block 基本上内存用

Claim

MEM_ROOT Claim 会对每个Block 进行Claim
而Block->claim, 主要是设置PSI

1
2
3
4
5
6
7
8
9
10
void my_claim(const void *ptr) {
my_memory_header *mh;

if (ptr == NULL) return;

mh = USER_TO_HEADER(ptr);
DBUG_ASSERT(mh->m_magic == MAGIC);
mh->m_key =
PSI_MEMORY_CALL(memory_claim)(mh->m_key, mh->m_size, &mh->m_owner);
}

Clear

如果设置了MY_MARK_BLOCKS_FREE或MY_KEEP_PREALLOC标记位, 则root->ClearForReuse();
否则直接调用root->Clear

ClearForReuse 和Clear 主要的区别就是, 对当前block的处理, Reuse 会重新使用当前Block, 而clear会清理当前Block
Clear

1
2
3
4
5
6
7
8
9
Block *start = m_current_block;

m_current_block = nullptr;
m_block_size = m_orig_block_size;
m_current_free_start = &s_dummy_target;
m_current_free_end = &s_dummy_target;
m_allocated_size = 0;

FreeBlocks(start);

ClearForReuse

1
2
3
4
5
6
7
m_current_free_start = pointer_cast<char *>(m_current_block) +
ALIGN_SIZE(sizeof(*m_current_block));
Block *start = m_current_block->prev;
m_current_block->prev = nullptr;
m_allocated_size = m_current_free_end - m_current_free_start;

FreeBlocks(start);

Block 内部

内存申请/释放都是走my_malloc/my_free/my_realloc, 这几个函数比较简单, 就是申请一块内存,然后将内存header设置相应的数据