MySQL 源码解读 -- innodb 内存

innodb 内存

数据结构

在innodb 内部, 用另外一个内存数据结构, mem_heap_t(也是mem_block_t)来表示
image.png
另外有allocator, 

mem_heap_allocator 负责mem_heap_t block的申请和释放, 做一个封装

ut_allocator 负责从_std::* containers申请内存, 内容比较简单, 提供一个封装, 封装alloc/dealloc_
_其中alloc时, 多申请_ut_new_pfx_t自己, 从而在ut_new_pfx_t 保存一些debug 信息

image.png

非buffer pool的内存情况(动态内存或者内存小于page_size/2)
image.png
mem_heap_t->start 指的位置是(char *)mem_heap_t + MEM_BLOCK_HEADER_SIZE

alignment

申请的(内存大小 + 32 个字节)然后向上取整8个字节

1
2
3
4
5
6
7
8
9

#define MEM_SPACE_NEEDED(N) \
ut_calc_align(N + 2 * MEM_NO_MANS_LAND, UNIV_MEM_ALIGNMENT)
#define MEM_BLOCK_HEADER_SIZE \
ut_calc_align(sizeof(mem_block_info_t), UNIV_MEM_ALIGNMENT)

#define ut_calc_align(n, m) (((n) + ((m)-1)) & ~((m)-1))
#define UNIV_MEM_ALIGNMENT 8
const int MEM_NO_MANS_LAND = 16;

Block 内部有很多alignment,
比如Block 的大小是  ut_calc_align(sizeof(mem_heap_t)) + MEM_SPACE_NEEDED(用户len)
用户每使用一块内存, 都会前后各空MEM_NO_MANS_LAND 个字节

使用

内存申请

函数mem_heap_alloc负责申请一块内存

mem_heap_add_block 可以参考创建过程中申请流程
assign 内存片段, 用户的内存前后,各保留了16各字节(MEN_NO_MANS_LAND)

1
2
3
4
free = mem_block_get_free(block);
buf = (byte *)block + free + MEM_NO_MANS_LAND;
mem_block_set_free(block, free + MEM_SPACE_NEEDED(n));
return buf;

创建

1
2
3
4
5
6
7
mem_heap_t * mem_heap_create_func() {
block = mem_heap_create_block(NULL, size, type, file_name, line);
UT_LIST_INIT(block->base, &mem_block_t::list);
UT_LIST_ADD_FIRST(block->base, block);
return block;
}

mem_heap_add_block 核心逻辑

1
2
3
4
5
6
7
8
9
10
mem_block_t *mem_heap_add_block(mem_heap_t *heap, /*!< in: memory heap */
ulint n) /*!< in: number of bytes user needs */
{
//set correct new size
new_size = xxxxx;
new_block = mem_heap_create_block(heap, new_size, heap->type, heap->file_name,
heap->line);
UT_LIST_INSERT_AFTER(heap->base, block, new_block);
return (new_block);
}

mem_heap_create_block 逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
mem_block_t *mem_heap_create_block_func(
mem_heap_t *heap, /*!< in: memory heap or NULL if first block
should be created */
ulint n, /*!< in: number of bytes needed for user data */
#ifdef UNIV_DEBUG
const char *file_name, /*!< in: file name where created */
ulint line, /*!< in: line where created */
#endif /* UNIV_DEBUG */
ulint type) /*!< in: type of heap: MEM_HEAP_DYNAMIC or
MEM_HEAP_BUFFER */
{
len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
if (type == MEM_HEAP_DYNAMIC || len < UNIV_PAGE_SIZE / 2) {
block = static_cast<mem_block_t *>(ut_malloc_nokey(len));
} else {
block = 从buffer_pool 中取
len = ((ulint)srv_page_size);
if ((type & MEM_HEAP_BTR_SEARCH) && heap) {
/* We cannot allocate the block from the
buffer pool, but must get the free block from
the heap header free block field */
buf_block = static_cast<buf_block_t *>(heap->free_block);
heap->free_block = NULL;
} else {
buf_block = buf_block_alloc(NULL);
}

block = (mem_block_t *)buf_block->frame;
}

// 设置block
block->buf_block = buf_block;
block->free_block = NULL;
block->magic_n = MEM_BLOCK_MAGIC_N;
mem_block_set_len(block, len);
mem_block_set_type(block, type);
mem_block_set_start(block, MEM_BLOCK_HEADER_SIZE);
mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE);

if (UNIV_UNLIKELY(heap == NULL)) {
block->total_size = len;
} else {
heap->total_size += len;
}
}

释放heap

先把heap->free_block释放掉, 从最后一个block开始free, 直到整个heap全部释放
mem_heap_block_free(heap, block); 这个函数释放的block不是从buffer pool里面申请的内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void mem_heap_block_free(mem_heap_t *heap,   /*!< in: heap */
mem_block_t *block){
UT_LIST_REMOVE(heap->base, block);
heap->total_size -= block->len;
block->magic_n = MEM_FREED_BLOCK_MAGIC_N;
if (type == MEM_HEAP_DYNAMIC || len < UNIV_PAGE_SIZE / 2) {
ut_allocator<byte>(PSI_NOT_INSTRUMENTED) \
.deallocate(reinterpret_cast<byte *>(block));
// 实际上就是把整个ut_new_pfx_t内存块给释放掉, 清掉了trace 字段
} else {
/* Make memory available again for buffer pool, as we set parts
of block to "free" state in heap allocator. */
UNIV_MEM_ALLOC(block, UNIV_PAGE_SIZE);
buf_block_free(buf_block);
///** Frees a buffer block which does not contain a file page. */
//buf_LRU_block_free_non_file_page(block);
}
}