前言
第一級是直接調用malloc
分配空間, 調用free
釋放空間, 第二級三就是創建一個內存池, 小於128字節的申請都直接在內存池申請, 不直接調用malloc
和free
. 本節分析第二級空間配置器, STL將第二級配置器設置爲默認的配置器, 因此只要一次申請的空間不超過128字節就默認在內存池中申請空間, 超過纔會調用第一級配置器.html
第二級配置器
首先先來介紹3個常量.c++
__ALIGN
: 以8字節進行對齊__MAX_BYTES
: 二級分配器最大分配的內存大小__NFREELISTS
: 128字節能分配的的鏈表個數, 而且從每一個鏈表保存的內存大小都是8的倍數, 並且都比前一個大8字節, 也就是分別是8, 16, 32...128字節
// 二級配置器 enum {__ALIGN = 8}; // 設置對齊要求. 對齊爲8字節, 沒有8字節自動補齊 enum {__MAX_BYTES = 128}; // 第二級配置器的最大一次性申請大小, 大於128就直接調用第一級配置器 enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; // 鏈表個數, 分別表明8, 16, 32....字節的鏈表
再介紹一個宏操做, 這是進行對齊操做, 將不滿8的倍數的填充成8的倍數.spa
static size_t FREELIST_INDEX(size_t bytes) \ {\ return (((bytes) + ALIGN-1) / __ALIGN - 1);\ }
從allocate先切入分析
- 先判斷申請的字節大小是否是大於128字節, 是, 則交給第一級配置器來處理. 否, 繼續往下執行
- 找到分配的地址對齊後分配的是第幾個大小的鏈表.
- 得到該鏈表指向的首地址, 若是鏈表沒有多餘的內存, 就先填充鏈表.
- 返回鏈表的首地址, 和一塊能容納一個對象的內存, 並更新鏈表的首地址
static void * allocate(size_t n) { obj * __VOLATILE * my_free_list; obj * __RESTRICT result; if (n > (size_t) __MAX_BYTES) { return(malloc_alloc::allocate(n)); } my_free_list = free_list + FREELIST_INDEX(n); result = *my_free_list; if (result == 0) // 沒有多餘的內存, 就先填充鏈表. { void *r = refill(ROUND_UP(n)); return r; } *my_free_list = result -> free_list_link; return (result); };
refill
內存填充.code
- 向內存池申請空間的起始地址
- 若是隻申請到一個對象的大小, 就直接返回一個內存的大小, 若是有更多的內存, 就繼續執行
- 從第二個塊內存開始, 把從內存池裏面分配的內存用鏈表給串起來, 並返回一個塊內存的地址給用戶
// 內存填充 template <bool threads, int inst> void* __default_alloc_template<threads, inst>::refill(size_t n) { int nobjs = 20; char * chunk = chunk_alloc(n, nobjs); // 向內存池申請空間的起始地址 obj * __VOLATILE * my_free_list; obj * result; obj * current_obj, * next_obj; int i; // 若是隻申請到一個對象的大小, 就直接返回一個內存的大小 if (1 == nobjs) return(chunk); my_free_list = free_list + FREELIST_INDEX(n); // 申請的大小不僅一個對象的大小的時候 result = (obj *)chunk; // my_free_list指向內存池返回的地址的下一個對齊後的地址 *my_free_list = next_obj = (obj *)(chunk + n); // 這裏從第二個開始的緣由主要是第一塊地址返回給了用戶, 如今須要把從內存池裏面分配的內存用鏈表給串起來 for (i = 1; ; i++) { current_obj = next_obj; next_obj = (obj *)((char *)next_obj + n); if (nobjs - 1 == i) { current_obj -> free_list_link = 0; break; } else { current_obj -> free_list_link = next_obj; } } return(result); }
再從deallocate結束
- 釋放的內存大於128字節直接調用一級配置器進行釋放
- 將內存直接還給對應大小的鏈表就好了, 並不用直接釋放內存, 以便後面分配內存的時候快速.
static void deallocate(void *p, size_t n) { obj *q = (obj *)p; obj * __VOLATILE * my_free_list; // 釋放的內存大於128字節直接調用一級配置器進行釋放 if (n > (size_t) __MAX_BYTES) { malloc_alloc::deallocate(p, n); return; } my_free_list = free_list + FREELIST_INDEX(n); q -> free_list_link = *my_free_list; *my_free_list = q; }
統一的接口
定義符合STL規格的配置器接口, 無論是一級配置器仍是二級配置器都是使用這個接口進行分配的htm
// 定義符合STL規格的配置器接口, 無論是一級配置器仍是二級配置器都是使用這個接口進行分配的 template<class T, class Alloc> class simple_alloc { public: static T *allocate(size_t n) { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); } static T *allocate(void) { return (T*) Alloc::allocate(sizeof (T)); } static void deallocate(T *p, size_t n) { if (0 != n) Alloc::deallocate(p, n * sizeof (T)); } static void deallocate(T *p) { Alloc::deallocate(p, sizeof (T)); } };
總結
用鏈表來保存不一樣字節大小的內存塊, 就很容易的進行維護, 並且每次的內存分配都直接能夠從鏈表或者內存池中得到, 提高了咱們申請內存的效率, 畢竟每次調用malloc和free效率是很低的, 特別是很小內存的時候.對象
STL默認的就是第二級配置器, 它會自動判斷咱們使用哪個配置器.blog
原文出處:https://www.cnblogs.com/0xfffffff0/p/10087810.html接口