linux記憶體分配管理
實現自己的malloc
stl是一套非常高效的c++庫,提到記憶體管理,怎麼能少了他呢,花了近一天的時間來剖析這個。
stl記憶體分配分為兩級,為什麼分為兩級,就比如你為了買一根普通的皮帶,去漢正街批發市場,別人不一定賣給你(親身經歷)。只有一次性購買大量的貨才會去批發市場,商家才會和你做生意。記憶體分配也是這個道理,當申請的記憶體大小大於128byte時,就啟動第一級分配器通過malloc直接向系統的堆空間分配,如果申請的記憶體大小小於128byte時,就啟動第二級分配器,從乙個預先分配好的記憶體池中取一塊記憶體交付給使用者,這個記憶體池由16個不同大小(8的倍數,8~128byte)的空閒列表組成,allocator會根據申請記憶體的大小(將這個大小round up成8的倍數)從對應的空閒塊列表取表頭塊給使用者。這樣做的好處有兩個:
1)小物件的快速分配。小物件是從記憶體池分配的,這個記憶體池是系統呼叫一次malloc分配一塊足夠大的區域給程式備用,當記憶體池耗盡時再向系統申請一塊新的區域,整個過程類似於批發和零售,起先是由allocator向總經商批發一定量的貨物,然後零售給使用者,與每次都總經商要乙個貨物再零售給使用者的過程相比,顯然是快捷了。當然,這裡的乙個問題時,記憶體池會帶來一些記憶體的浪費,比如當只需分配乙個小物件時,為了這個小物件可能要申請一大塊的記憶體池,但這個浪費還是值得的,況且這種情況在實際應用中也並不多見。
2)避免了記憶體碎片的生成。程式中的小物件的分配極易造成記憶體碎片,給作業系統的記憶體管理帶來了很大壓力,系統中碎片的增多不但會影響記憶體分配的速度,而且會極大地降低記憶體的利用率。以記憶體池組織小物件的記憶體,從系統的角度看,只是一大塊記憶體池,看不到小物件記憶體的分配和釋放。
關於第一級記憶體分配,前面的文章已經講過,這篇著重講解次級記憶體分配的機制。
次級記憶體是針對分配1~128位元組的記憶體而啟用的。我們看到這個範圍還是挺廣的,stl為了更好的管理記憶體分配,就針對這個128位元組再次分組,就相當於乙個小菜市場裡面有很多檔口一樣。每8位元組一組,也就是說分配[1,8]位元組記憶體為一組,即在同乙個線性空間裡面,畢竟乙個容器元素的型別都一樣。同理後面的[9,16],[17,24]。。。為一組。分組就是按照傳入的引數大小按照這個方式來分組。也就是針對不同的組有不同的入口,每一組由是相同大小的塊組成。示意圖如下:
注意,從上面的示意圖可以看出,這種方式的分配記憶體也是會有記憶體碎片的,因為分配記憶體時會向上取整到8的整數倍。究竟是怎麼管理記憶體的分配與釋放的呢,假設每次分配的都是小於8位元組的記憶體,記憶體的分配和釋放都在第一路上執行,以下是分配管理的示意圖:
結合上面的圖來描述,整體過程是這樣的:
一開始時,沒有分配任何空間,就去分配一定數量的固定8位元組大小的記憶體塊,比如20個8位元組的塊。我們先分配兩倍大小的記憶體,即40*8位元組,然後將前20個位元組按照8位元組分割,剩餘的塊留著備用。分割的目的是為了按鍊錶的方式索引。一般的記憶體分配管理也會用到鍊錶來索引下一塊記憶體,鍊錶會占用額外的記憶體。這裡鍊錶卻沒有占用額外的記憶體,因為鍊錶的值直接在記憶體塊中,具體的看下面的源**就能明白。
釋放記憶體也非常簡單,直接將釋放的記憶體作為free記憶體塊鍊錶的頭結點,以待下次使用。順便說一句,一般的記憶體釋放會是沒有大小這個引數的,因為分配記憶體時有個頭結點儲存了這個信。而stl物件自己儲存了物件的大小,再者在記憶體中儲存這個資訊是要佔額外空間的,stl的記憶體管理還是相當的省。所以stl的記憶體分配和釋放很適合於它這個分配小物件的場景,並不是太通用。
完整的**如下:
#include class alloc
; enum ;
enum ;
enum ;
static obj* free_list[__nfreelists];
private:
static char* start_free;
static char* end_free;
static size_t heap_size;
private:
static size_t round_up(size_t bytes)
static size_t freelist_index(size_t bytes)
static void* refill(size_t n);
static void* chunk_alloc(size_t size, int& nobjs);
public:
static void* allocate(size_t bytes);
static void deallocate(void* ptr, size_t bytes);
static void* reallocate(void* ptr, size_t old_sz, size_t new_sz);
};alloc::obj* alloc::free_list[__nfreelists] = ;
char* alloc::start_free = 0;
char* alloc::end_free = 0;
size_t alloc::heap_size = 0;
void* alloc::allocate(size_t n)
//鍊錶中沒有free空間了就重新分配空間,否則直接使用,然後指向下一塊free空間
my_free_list = free_list + freelist_index(n);
result = *my_free_list;
if (0 == result)
*my_free_list = result->next;
return result;
}void alloc::deallocate(void* p, size_t n)
my_free_list = free_list + freelist_index(n);
obj* q = static_cast(p);
q->next = *my_free_list;
*my_free_list = q;
}void* alloc::refill(size_t n)
//分割記憶體塊
void* res = chunk;
my_free_list = free_list + freelist_index(n);
obj* next = (obj*)(chunk + n);
*my_free_list = next;
obj* current;
int i = 1;
for(;;++i)
else
}return res;
}void* alloc::chunk_alloc(size_t size, int& nobjs)
else if (bytes_left >= size)
else
start_free = static_cast(malloc(bytes_to_get));
if (0 == start_free)
}end_free = 0;
start_free = static_cast(malloc(bytes_to_get));
}heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
return (chunk_alloc(size, nobjs));
}}void* alloc::reallocate(void* p, size_t old_size, size_t new_size)
int main()
參考: STL 記憶體管理
stl有很多種allocator,根據c 的標準,stl的allocator把物件的申請和釋放分為四個步驟 1 申請記憶體空間,對應的函式是allocator allocate 2 執行建構函式,對應的函式是allocator construct 3 執行析構函式,對應的函式是allocator d...
STL記憶體管理
過年在家無事看了 stl原始碼剖析 開學了將看過的東西總結一下,以防忘記。先從stl的記憶體管理開始總結。掌管stl記憶體控制的是乙個叫空間介面卡 alloc 的東西。stl有兩個空間介面卡,sgi標準空間介面卡 allocate 和sgi特殊的空間介面卡 alloc 前者只是對c 的new和del...
stl記憶體管理
stl提供了很多泛型容器,如vector,list和map。程式設計師在使用這些容器時只需關心何時往容器內塞物件,而不用關心如何管理記憶體,需要用多少記憶體,這些stl容器極大地方便了c 程式的編寫。例如可以通過以下語句建立乙個vector,它實際上是乙個按需增長的動態陣列,其每個元素的型別為int...