這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
昨天寫了一篇Go語言記憶體 Clerk設計,記錄了一下記憶體 Clerk的大體結構。在介紹記憶體 Clerk的核心實現前,本文先介紹一下記憶體 Clerk中一個工具組件——FixAlloc。FixAlloc稱不上是核心組件,只是輔助實現整個記憶體 Clerk核心的一個基礎工具罷了,由此可以看出FixAlloc還是一個比較重要的組件。引入FixAlloc的目的只是用來分配MCache
和MSpan
兩個特定的對象,所以記憶體 Clerk中有spanalloc
和cachealloc
兩個組件(見《Go語言記憶體 Clerk設計》的圖)。MCache和MSpan兩個結構在malloc.h中有定義。
定義在malloc.h檔案中的FixAlloc結構如下,比較關鍵的三個欄位是alloc、list和chunk,其他的欄位主要都是用來統計一些狀態資料的,比如分配了多少記憶體之類。
struct FixAlloc{uintptr size;void *(*alloc)(uintptr);void (*first)(void *arg, byte *p);// called first time p is returnedvoid *arg;MLink *list;byte *chunk;uint32 nchunk;uintptr inuse;// in-use bytes nowuintptr sys;// bytes obtained from system};
FixAlloc的記憶體結構圖,一看就很簡單,簡單到沒有出現本文的必要了。
list
指標上掛的一個鏈表,這個鏈表的每個節點是一個固定大小的記憶體塊,cachealloc中的list儲存的記憶體塊大小為sizeof(MCache)
,而spanalloc中的list儲存的記憶體塊大小為sizeof(MSpan)
。chunk
指標始終掛載的是一個128k大的記憶體塊。
FixAlloc提供了三個API,分別是runtime·FixAlloc_Init、runtime·FixAlloc_Alloc和runtime·FixAlloc_Free。
分配一個mcache和mspan的虛擬碼:
MCache *mcache;mcache = (MCache *) runtime·FixAlloc_Alloc(cachealloc);MSpan *mspan;mspan = (MSpan *) runtime·FixAlloc_Alloc(spanalloc);
這段虛擬碼展示的是分配一個MCache和MSpan對象,記憶體 Clerk並不是直接使用malloc類函數向系統申請,而是走了FixAlloc。使用FixAlloc分配MCache和MSpan對象的時候,首先是尋找FixAlloc的list鏈表,如果list不為空白,就直接拿一個記憶體塊返回使用; 如果list為空白,就把焦點轉移到chunk上去,如果128k的chunk記憶體中有足夠的空間,就切割一塊記憶體出來返回使用,如果chunk記憶體沒有剩餘記憶體的話,就從作業系統再申請128k記憶體替代老的chunk。FixAlloc的固定對象分配邏輯就這麼簡單,相反釋放邏輯更簡單了,釋放的對象就是直接放到list中,並不會返回給作業系統。當然mcache的個數基本是穩定的,也就是底層線程個數,但span對象就不一定那麼穩定了,所以FixAlloc的記憶體可能增長的因素就是span的對象太多。
FixAlloc的實現位於mfixalloc.c檔案中,代碼目前還不到100行,實在是太簡單了。本來是計劃本文一起介紹完FixAlloc和MSpan兩個基礎組件,今天身體不舒服,小感冒了,沒精力再寫MSpan了。
註:本文基於Go1.1.2版本。
***下班躺在床上,用小米盒子一邊看著無聊的電視劇,一邊抱著電腦寫技術文章,貌似也可以讓自己處於一種非常輕鬆的狀態,前提可能是自己對需要寫的東西了如指掌,不再需要去翻代碼吧。