在innodb中實現了自己的內存池系統和內存堆分配系統,在innodb的內存管理系統中,大致分爲三個部分:基礎的內存塊分配管理、內存夥伴分配器和內存堆分配器。innodb定義和實現內存池的主要目的是提供內存的使用率和效率,防止內存碎片和內存分配跟蹤和調試。我們先來看看他們的關係和結構。
以下是它的關係結構圖:
上圖中的:
ut_mem_block塊是基礎內存管理
Buddy allocator是內存夥伴分配器
mem_heap是內存堆分配器
1.基礎內存管理
typedef ut_mem_block_struct
{
ulint size; /*這個被分配block的內存大小*/
ulint magic_n; /*節點魔法字,用於校驗所用*/
UT_LIST_NODE_T(ut_mem_block_t) mem_block_list; /*block list node,指定prev node和next node*/
};
關於block的list定義是個全局的變量,UT_LIST_BASE_NODE_T(ut_mem_block_t)
ut_mem_block_list;所有分配的block都會加入到這個list當中。在ut_malloc_low函數分配內存的時候會將分配的block加入到list當中,在ut_free的時候會所釋放的內存所在的block從list當中刪除。除了這兩個函數以外,innodb還提供ut_free_all_mem函數來釋放所有分配的block和統計分配內存的總數ut_total_allocated_memory功能。ut_malloc_low 分配一個n長度的內存塊,並將分配的塊記錄到ut_mem_block_list當中.
ut_malloc 與ut_malloc_low功能相同,但是會用0初始化所分配的內存。
ut_free 釋放一個分配的內存塊,並將其從ut_mem_block_list當中刪除。
ut_free_all_mem 釋放ut_mem_block_list所有的內存塊並清空ut_mem_block_list
以上函數是支持多線程併發操作的,也就是說是線程安全的。
2.夥伴分配器
struct mem_pool_struct
{
byte* buf; /*整體內存的句柄*/
ulint size; /*整體內存大小*/
ulint reserved; /*當前分配出去的總內存大小*/
mutex mutex; /*多線程互斥量*/
UT_LIST_BASE_NODE_T(mem_area_t) free_list[64]; /*area_t鏈表數組,每個數組單元能管理2的i次方內存塊列表,i是數組的下標*/
};
struct mem_area_struct
{
ulint size_and_free; /*area的內存大小(一定是2的次方),最後一個bit表示是否已經釋放*/
UT_LIST_NODE_T(mem_area_t) free_list; /*area鏈表的上下area,因爲buddy area是會分裂的,有可能多個*/
};
mem_area_t是一個buddy的內存區域,也就是mem_area_struct。以下是一個32位機器管理1024字節內存塊的buddy list分佈:2.1mem_area_t的分裂
2.2mem_area_t的合併
1.使用者歸還夥伴分配的內存,首先會根據area_t的信息去找到自己的buddy,也就是第8層另外一個沒有被分配的area.
2.找到buddy area後,判斷buddy area是否是釋放狀態,如果是,觸發合併,將自己和buddy area從第8層刪除,合併成一個512大小的第9層area,
3.在重複1 ~ 2步,又會將自己和第九層另外一個buddy area合併成一個1024大小的第10層area.
2.3buddy allocator的接口函數:
mem_area_alloc 用buddy allocator分配一塊內存
mem_area_free 將一塊內存歸還給buddy allocator
mem_pool_get_reserved 獲得buddy allocator已經使用的內存大小
3內存分配堆(memory heap)
struct mem_block_info_struct
{
ulint magic_n; /*魔法字*/
char file_name[8]; /*分配內存的文件*/
ulint line; /*分配內存的文件所在行*/
ulint len; /*block的長度*/
ulint type; /*依賴的底層分配類型,有DYNAMIC、BUFFER、BTR_SEARCH三種類型*/
ibool init_block; /*是否是外部分配的內存塊*/
ulint free; /*被佔用的空間大小*/
ulint start; /*可分配內存的起始位置*/
byte* free_block; /*備用block,僅僅在BTR_SEARCH方式可用*/
UT_LIST_BASE_NODE_T(mem_block_t) base;
UT_LIST_NODE_T(mem_block_t) list;
};
備註:mem_block_info_struct/mem_block_info_t/mem_block_t/mem_heap_t是等價mem_heap_t的內存結構如下:
1.一個mem_block_t最小空間不小於64字節,標準的大小是8KB,在非MEM_HEAP_BUFFER模式下分配的空間不大於page size - 200(page size一般爲16KB)
2.mem_heap_t有三種類型,分別是DYNAMIC、BUFFER、BTR_SEARCH,在DYNAMIC模式下都是基於buddy allocator進行mem_block_t分配的,在BTR_SEARCH模式下,使用free_block來作爲內存分配,在BUFFER模式下比較複雜,如果分配的內存大小< page size的一半時,使用buddy alloc,否則使用buf_frame的內存分配方式(這個是屬於buf0buf.XX裏面的方式,還未開始分析)。
3.mem_heap_t在分配新的mem_block_t的時候一定是分配一個heap最後節點大小的兩倍,如果分配的大小超過MEM_MAX_ALLOC_IN_BUF(相當於一個page size)的時候,heap 類型判斷,在不是DYNAMIC模式下,最大就是一個MEM_MAX_ALLOC_IN_BUF大小。如果其他模式下就是設置成MEM_BLOCK_STANDARD_SIZE標準大小,在這些限制外,如果需要分配的內存大於這些限制,以分配內存大小爲準進行mem_block_t分配。分配好的mem_block_t總是加入到heap base list的最後,也就是heap堆棧的頂端。
4.mem_heap_t在釋放mem_block_t時候總是從頂端開始釋放,直到不能釋放爲止(mem_block_t沒有被使用者歸還)。在mem_block_t釋放的時候也是需要參考DYNAMIC、BUFFER、BTR_SEARCH類型進行相對於的歸還規則(和2要點是相對應的)。
mem_heap_create 用DYNAMIC模式創建一個mem_heap_t
mem_heap_create_in_buffer 用BUFFER模式創建一個mem_heap_t
mem_heap_create_in_btr_search 用BTR_SEARCH模式創建一個mem_heap_t
mem_heap_free 釋放mem_heap_t對象
mem_alloc 創建在MEM_HEAP_DYNAMIC模式下,並分配一塊指定大小的內存(在這種方式下mem_heap_t只會有一個mem_block_t)
mem_free 歸還mem_heap_t分配的內存,並釋放mem_heap_t
mem_heap_alloc 在指定的mem_heap_t上分配一塊內存
mem_heap_get_heap_top 獲得heap頂端塊可使用內存的地址
mem_heap_empty 清空指定的mem_heap_t
mem_heap_get_top 獲得heap頂部的指定n大小的mem_block_t指針
mem_heap_free_top 釋放heap頂部N大小的mem_block_t塊