1. 程式人生 > >STL源碼分析之內存池

STL源碼分析之內存池

重新 其他 except 多次調用 源碼 stl lis 他能 data

前言

上一節只分析了第二級配置器是由多個鏈表來存放相同內存大小, 當沒有空間的時候就向內存池索取就行了, 卻沒有具體分析內存池是怎麽保存空間的, 是不是內存池真的有用不完的內存, 本節我們就具體來分析一下

內存池

static data template的初始化

template <bool threads, int inst>
char *__default_alloc_template<threads, inst>::start_free = 0;  // 內存池的首地址
template <bool threads, int inst>
char *__default_alloc_template<threads, inst>::end_free = 0;    // 內存池的結束地址
template <bool threads, int inst>
size_t __default_alloc_template<threads, inst>::heap_size = 0;  // 多次調用內存池, 就會更多的是給鏈表分配內存, 這就是一個增量.

這裏代碼註釋寫的很清楚了, 我就提取出來分析一下吧

  1. 內存池的大小大於需要的空間, 直接返回起始地址(nobjs默認設置為20, 所以每次調用都會給鏈表額外的19個內存塊)
  2. 內存池的內存不足以馬上分配那麽多內存, 但是還能滿足分配一個即以上的大小, 那就全部分配出去
  3. 如果一個對象的大小都已經提供不了了, 先將零碎的內存塊給一個小內存的鏈表來保存, 然後就準備調用malloc申請40塊+額外大小的內存塊(額外內存塊就由heap_size決定), 如果申請失敗跳轉到步驟4, 成功跳轉到步驟6
  4. 充分利用更大內存的鏈表, 通過遞歸來調用他們的內存塊
  5. 如果還是沒有內存塊, 直接調用一級配置器來申請內存, 還是失敗就拋出異常, 成功申請就繼續執行
  6. 重新修改內存起始地址和結束地址為當前申請的地址塊, 重新調用chunk_alloc分配內存
// 內存池
template <bool threads, int inst>
char* __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;    // 內存池裏面總共還有多少內存空間

      // 內存池的大小大於需要的空間, 直接返回起始地址
    if (bytes_left >= total_bytes) 
    {
        result = start_free;
        start_free += total_bytes;  // 內存池的首地址往後移
        return(result);
    }
    // 內存池的內存不足以馬上分配那麽多內存, 但是還能滿足分配一個即以上的大小, 那就按對齊方式全部分配出去
    else if (bytes_left >= size) 
    {
        nobjs = bytes_left/size;
        total_bytes = size * nobjs;
        result = start_free;
        start_free += total_bytes;  // 內存池的首地址往後移
        return(result);
    } 
    else 
    { 
        // 如果一個對象的大小都已經提供不了了, 那就準備調用malloc申請兩倍+額外大小的內存
        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
        // Try to make use of the left-over piece.
        // 內存池還剩下的零頭內存分給給其他能利用的鏈表, 也就是絕不浪費一點.
        if (bytes_left > 0) 
        {
            // 鏈表指向申請內存的地址
            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;
            // 充分利用剩余鏈表的內存, 通過遞歸來申請
            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));
                }
            }
            // 如果一點內存都沒有了的話, 就只有調用一級配置器來申請內存了, 並且用戶沒有設置處理例程就拋出異常
            end_free = 0;   // In case of exception.
            start_free = (char *)malloc_alloc::allocate(bytes_to_get);
        }
            // 申請內存成功後重新修改內存起始地址和結束地址, 重新調用chunk_alloc分配內存
            heap_size += bytes_to_get;
            end_free = start_free + bytes_to_get;
            return(chunk_alloc(size, nobjs));
    }   
}

總結

內存池的存在就是為了能快速的提供我們做需要的內存並且保存多余的空間, 讓STL分配空間不再每次都進行malloc和free的操作, 效率又很有保障. 有時用戶申請的塊更小, 我們也能充分的利用起來. 唯一可能不足的是我們每次只申請char個大小, 但是內存池獲得的確是8字節的大小.

STL源碼分析之內存池