memory manager

innobaseのmemory managerはmemory poolとmemory heapで構成されている。
memory poolは、最初にmysqlのパラメータinnobase_additional_mem_pool_sizeで指定された大きさのバッファを確保して、そこから必要なメモリを割り当てていく。このバッファからメモリを割り当てることが出来なくなったら、ut_mallocでメモリを割り当てる。(ut_mallocmallocのラッパ)
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を開放