TCMalloc的使用與原始碼剖析之三---------TCMalloc的記憶體分配的主要層次
(1)第一層,執行緒區域性分配,ThreadCache
ThreadCache包含了一個不同物件大小的空閒連結串列陣列,其實現採用作業系統的執行緒區域性儲存功能。分配時幾乎不需要用鎖,除非觸發CentralCache的操作。
ThreadCache中的重要資料結構:
pthread_t tid_; 繫結執行緒,達到每個執行緒有個緩衝池的目的
FreeList list_[kNumClasses]; 這個陣列就是上圖中的第一列,如下圖
陣列中的每一個節點就是代表上圖中的每一行,如下圖
每個class對應多大的記憶體空間?這個表示每組的大小的變數在哪裡?
不存在這樣的變數,但是通過對映關係可以達到一個class管一類size的作用,
如下圖所示,由cl 得到list_[cl],這也即是一個class。
至於cl,是由class_array_得到的,關於這個內容在第五章:幾個重要的資料結構
若申請的記憶體是13位元組,但分配的卻是15位元組,那麼便會有2個位元組的記憶體碎片(內部碎片)。
(2)第二層 ,中心分配,Centralcache
該層的分配需要鎖。CentralCache和ThreadCache之間的空閒連結串列是一一對應的,以子連結串列為單位(obj個數很可能為num_objects_to_move(cl),見do_malloc
CentralCache的記憶體從PageHeap裡獲得。從PageHeap獲得的記憶體叫Span。一個Span在使用時只能用於同一大小的空閒連結串列,一但CentralCache從PageHeap中獲取新的Span,這個Span就是一個串好的相同大小記憶體的空閒連結串列。
Centralcache中有幾個重要資料結構:
1. TCEntry tc_slots_[kMaxNumTransferEntries];
tc_slots_每個節點存放的是一組obj連結串列,這一組obj的個數為num_objects_to_move,TCEntry結構體有兩指標,分別指向這個連結串列的頭和尾。
tc_slots_存放的是threadCache向CentralCache歸還的obj連結串列,並且只有當個數滿足num_objects_to_move時,才會放入tc_slots_。否則歸還的obj根據其所處的span,進行歸還,若對應的span是empty,那麼由於此時被歸還記憶體了,所以其有空閒obj了,便把該span從empty佇列清除,把其加入nonempty佇列。
2. span empty
FetchFromSpans函式把一個obj從nonempty佇列中的一個span中切出,準備給threadCache。當切完這個obj後,如果該span已經沒有記憶體空間了,那麼便把該span從nonempty佇列移除,並加入empty佇列。
3. span nonempty
CentralCache從中央頁堆申請頁面,中央頁堆以span的形式返回。在CentralCache中會把該span切成大小為class_to_size的obj,並把所有的obj連結起來,連結串列頭為span->objects。再把該連結串列加入nonempty佇列。
Nonempty佇列另外一個被加入span的地方在記憶體從threadCache歸還給CentralCache時,具體情況見上面”tc_slots_”這一資料結構的描述。
(3)第三層,中央頁堆,PageHeap
PageHeap以一定數量連續頁面記憶體的形式提供記憶體。這組連續的頁面由一個Span物件描述,Span物件和它描述的頁面記憶體是獨立的。Span物件儲存了頁面的id序列,頁面id左移一個page就是記憶體的地址。由於頁面和Span記憶體獨立,需要用page id反向對映查詢Span物件就需要單獨的對映表。這個表用radix tree實現,兼顧效率和記憶體。PageHeap還負責合併和拆分相鄰的Span。
PageHeap重要資料結構:
SpanList large_;
SpanList free_[kMaxPages];
中央頁堆是由空閒記憶體列表組成的陣列。對於i< 256而言,陣列的第k個元素是一個由每個單元是由k個頁面組成的空閒記憶體連結串列(這也即是free_)。第256個條目(這即是large_)則是一個包含了長度>= 256個頁面的空閒記憶體連結串列:
而SpanList為
struct SpanList {
Span normal;
Span returned;
};
Returned代表的是已經歸還給系統的span
(4)第四層,系統頁面分配,
這就是呼叫系統函數了。