memory manager
innobaseのmemory managerはmemory poolとmemory heapで構成されている。
memory poolは、最初にmysqlのパラメータinnobase_additional_mem_pool_sizeで指定された大きさのバッファを確保して、そこから必要なメモリを割り当てていく。このバッファからメモリを割り当てることが出来なくなったら、ut_mallocでメモリを割り当てる。(ut_mallocはmallocのラッパ)
memory heapはmemory poolからメモリを割り当てて作成される。
mem_pool_create
memory poolを作成する。sizeはバッファサイズ。
(1)ut_mallocでmem_pool_tのメモリを割り当てる
pool = ut_malloc(sizeof(mem_pool_t));
(2)ut_malloc_lowでpool->bufのメモリを割り当てる
pool->buf = ut_malloc_low(size, FALSE, TRUE);
2番目の引数をFALSEにして、確保したメモリを0で初期化しないようにしている
(3)pool->sizeを設定
(4)pool->mutexを作成
(5)freeリストを初期化(64個ある)
for (i = 0; i < 64; i++) { UT_LIST_INIT(pool->free_list[i]); }
(6)free listにメモリを登録
できるだけ大きいメモリブロック単位で、リストに登録する
used = 0; while (size - used >= MEM_AREA_MIN_SIZE) { i = ut_2_log(size - used); if (ut_2_exp(i) > size - used) { /* ut_2_log rounds upward */ i--; } area = (mem_area_t*)(pool->buf + used); mem_area_set_size(area, ut_2_exp(i)); mem_area_set_free(area, TRUE); UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area); used = used + ut_2_exp(i); }
(7)pool->reservedを設定
(8)poolを返す
mem_pool_fill_free_list
iで指定したfree listよりも大きいサイズのfree listに登録されているメモリを分割して、iのfree listに割り当てる。
(1)free_list[i + 1]のメモリを取得
area = UT_LIST_GET_FIRST(pool->free_list[i + 1]);
(2)free_list[i + 1]が空だったら、mem_pool_fill_free_listを再帰コールして、さらに上のリストのメモリを、free_list[i + 1]に割り当てる
if (area == NULL) { ret = mem_pool_fill_free_list(i + 1, pool); if (ret == FALSE) { return(FALSE); } area = UT_LIST_GET_FIRST(pool->free_list[i + 1]); }
(3)free list[i + 1]から先頭のメモリ(area)を削除する
UT_LIST_REMOVE(free_list, pool->free_list[i + 1], area);
(4)areaを2分割してfree list[i]に登録
// 後ろ半分の処理 area2 = (mem_area_t*)(((byte*)area) + ut_2_exp(i)); // 位置を計算 mem_area_set_size(area2, ut_2_exp(i)); // サイズを設定 mem_area_set_free(area2, TRUE); // free flagを設定 UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area2); // listに登録 // 前半分の処理 (free flagは変更する必要がない) mem_area_set_size(area, ut_2_exp(i)); // サイズを設定 UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area); // listに登録
mem_area_alloc
memory poolからメモリを割り当てる
(1)使用するfree listを計算
n = ut_2_log(ut_max(size + MEM_AREA_EXTRA_SIZE, MEM_AREA_MIN_SIZE));
(2)mutexをロック
mutex_enter(&(pool->mutex)); mem_n_threads_inside++;
(3)free_list[n]からmemory areaを取得
area = UT_LIST_GET_FIRST(pool->free_list[n]);
(4)free_list[n]が空だった場合、mem_pool_fill_free_listでfree_list[n + 1]以降から、free_list[n]にメモリを割り当てる
free_list[n + 1]以降からメモリが割り当てられなかった場合、ut_mallocでメモリを割り当てて返す。
if (area == NULL) { ret = mem_pool_fill_free_list(n, pool); if (ret == FALSE) { /* Out of memory in memory pool: we try to allocate from the operating system with the regular malloc: */ mem_n_threads_inside--; mutex_exit(&(pool->mutex)); return(ut_malloc(size)); } area = UT_LIST_GET_FIRST(pool->free_list[n]); }
(5)使用するmemory areaのfree flagを降ろす
mem_area_set_free(area, FALSE);
(6)memory areaをfree_list[n]から削除
UT_LIST_REMOVE(free_list, pool->free_list[n], area);
(7)pool->reservedを増加させる
pool->reserved += mem_area_get_size(area);
(8)mutexを開放
mem_n_threads_inside--; mutex_exit(&(pool->mutex));
(9)確保したメモリを返す
areaの先頭はstruct mem_area_structの領域なので、その後ろのポインタを返す。
return((void*)(MEM_AREA_EXTRA_SIZE + ((byte*)area)));
mem_area_get_buddy
memory areaの兄弟エリアを取得する
(1)areaとbufの差が(2 * size)で割り切れるとき、buddyは右(higher address)にある
if (((((byte*)area) - pool->buf) % (2 * size)) == 0) { /* The buddy is in a higher address */ buddy = (mem_area_t*)(((byte*)area) + size); // pool->bufをオーバーしないようにする if ((((byte*)buddy) - pool->buf) + size > pool->size) { /* The buddy is not wholly contained in the pool: there is no buddy */ buddy = NULL; }
(2)areaとbufの差が(2 * size)で割り切れないとき、buddyは左(lower address)にある
} else { /* The buddy is in a lower address; NOTE that area cannot be at the pool lower end, because then we would end up to the upper branch in this if-clause: the remainder would be 0 */ buddy = (mem_area_t*)(((byte*)area) - size); }
mem_area_free
mem_area_allocで割り当てたメモリを開放する。
(1)ptrがpool->buf〜(pool->buf + pool->size)の間にない場合、ut_mallocで
割り当てられたメモリなので、ut_freeを実行する。
if ((byte*)ptr < pool->buf || (byte*)ptr >= pool->buf + pool->size) { ut_free(ptr); return; }
(2)ptrからmemory areaのポインタを計算
area = (mem_area_t*) (((byte*)ptr) - MEM_AREA_EXTRA_SIZE);
(3)memory areaのサイズを取得
size = mem_area_get_size(area);
(4)memory areaの兄弟area(隣のarea)を取得
buddy = mem_area_get_buddy(area, size, pool);
(5)free_listのindexを計算
n = ut_2_log(size);
(6)mutexをロック
(7)兄弟areaが存在して、兄弟areaが未使用(mem_area_get_freeがTRUE)で、
兄弟areaのサイズがfreeするareaと同じとき、2つのareaをくっつけて
上位のfree_list[n + 1]に登録する。
if (buddy && mem_area_get_free(buddy) && (size == mem_area_get_size(buddy))) { /* The buddy is in a free list */ if ((byte*)buddy < (byte*)area) { // buddyがareaの左にある // buddyをfree_list[n + 1]に登録するための準備 new_ptr = ((byte*)buddy) + MEM_AREA_EXTRA_SIZE; mem_area_set_size(buddy, 2 * size); mem_area_set_free(buddy, FALSE); } else { // buddyがareaの右にある // ptrをfree_list[n + 1]に登録するための準備 new_ptr = ptr; mem_area_set_size(area, 2 * size); } /* Remove the buddy from its free list and merge it to area */ // buddyをfree_list[n]から削除 UT_LIST_REMOVE(free_list, pool->free_list[n], buddy); pool->reserved += ut_2_exp(n); mem_n_threads_inside--; mutex_exit(&(pool->mutex)); // mem_area_freeを再帰コールして、free_list[n + 1]に登録する mem_area_free(new_ptr, pool); return;
(8)buddyが使えない場合((7)の条件を満たさない場合)、ptrをfree_list[n]に登録する
UT_LIST_ADD_FIRST(free_list, pool->free_list[n], area); mem_area_set_free(area, TRUE); ut_ad(pool->reserved >= size); pool->reserved -= size;
(9)mutexを開放