1. 程式人生 > >SGISTL原始碼閱讀三 空間配置器下(記憶體池memory pool)

SGISTL原始碼閱讀三 空間配置器下(記憶體池memory pool)

SGISTL原始碼閱讀三 空間配置器下(記憶體池memory pool)

前言

在上一個部落格我們講述了空間配置器的第二級配置器,它的關鍵點free-lists是依賴於記憶體池的。在refill中我們通過呼叫chunk_alloc函式來申請區塊,chunk_alloc的作用就是從記憶體池中取空間給free-lists使用。下面我們通過原始碼來解讀它。

深入原始碼

template <bool threads, int inst>
char*
//注意,此處nobjs傳的是引用
__default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs)
{
    char * result;
    size_t total_bytes = size * nobjs;
    size_t bytes_left = end_free - start_free;
	//如果剩餘空間大於所申請的空間(nobjs個size大小)
    if (bytes_left >= total_bytes) {
    	//調整記憶體池的起始地址後返回result
        result = start_free;
        start_free += total_bytes;
        return(result);
    //如果剩餘空間大於一個size
    } else if (bytes_left >= size) {
    	//調整nobjs
        nobjs = bytes_left/size;
        total_bytes = size * nobjs;
        result = start_free;
        //調整記憶體池的起始地址,將記憶體池中所有的空間都返回給free-lists使用
        start_free += total_bytes;
        return(result);
    //記憶體池中的空間不夠了(重點)
    } else {
    	//這裡我們可以看到記憶體池向記憶體所申請的空間是total_bytes的兩倍+附加量
        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
        // 如果記憶體池不為空,將其置為空(把所有容量都供free-lists使用)
        if (bytes_left > 0) {
        	//找到對應的free-list並新增進去
            obj * __VOLATILE * my_free_list =
                        free_list + FREELIST_INDEX(bytes_left);

            ((obj *)start_free) -> free_list_link = *my_free_list;
            *my_free_list = (obj *)start_free;
        }
        //向記憶體中申請空間
        start_free = (char *)malloc(bytes_to_get);
        //如果失敗了
        if (0 == start_free) {
            int i;
            obj * __VOLATILE * my_free_list, *p;
            // Try to make do with what we have.  That can't
            // hurt.  We do not try smaller requests, since that tends
            // to result in disaster on multi-process machines.
            //從區塊大小更大的free-list中去獲取容量
            for (i = size; i <= __MAX_BYTES; i += __ALIGN) {
                my_free_list = free_list + FREELIST_INDEX(i);
                p = *my_free_list;
                if (0 != p) {
                    *my_free_list = p -> free_list_link;
                    //調整記憶體池的起始地址,並進行遞迴操作
                    start_free = (char *)p;
                    end_free = start_free + i;
                    return(chunk_alloc(size, nobjs));
                    // Any leftover piece will eventually make it to the
                    // right free list.
                }
            }
            //如果失敗了,就只能呼叫第一級配置器,從oom中尋求解決方案
            //(如果oom機制也沒有解決,那麼會丟擲一個異常,詳見第一級配置器https://blog.csdn.net/lyn_00/article/details/83929578)
            end_free = 0;       // In case of exception.
            start_free = (char *)malloc_alloc::allocate(bytes_to_get);
            // This should either throw an
            // exception or remedy the situation.  Thus we assume it
            // succeeded.
        }
        //走到這裡,說明已經成功申請了total_bytes的兩倍+附加量的空間
        heap_size += bytes_to_get;
        end_free = start_free + bytes_to_get;
        //最後遞迴呼叫自己,修正nobjs的值
        return(chunk_alloc(size, nobjs));
    }
}

簡要描述一下chunk_alloc的機制
1.如果記憶體池中的容量足夠那麼直接將total_bytes(size*nobjs)的空間供free-lists使用。
2.如果記憶體池中的容量沒那麼多,但是超過一個size大小,就改變nobjs的值,將記憶體池中所有的空間給free-lists使用。
3.如果記憶體池中的容量連一個size大小都沒有(這種情況是最複雜的)
(1)將記憶體池中的容量置零
(2)申請total_bytes的兩倍+附加量這麼大的空間,如果申請成功了,遞迴呼叫它本身,將申請到的容量一半給free-lists,一半存留在記憶體池中,如果申請失敗,見(3)
(3)在free-lists中尋找更大的free-list收回至記憶體池,再遞迴呼叫它本身。如果失敗,見(4)
(4)這種情況就是最壞的情況了,我們把它交給第一級配置器的oom機制去處理,最壞的結果是丟擲異常。

總結

之前在學習第二級配置器的時候有一個疑惑,現在就清楚了,記憶體池中處理了申請不到記憶體的情況。
現在我們對SGISTL的配置器應該也有了一個比較深入的理解。