歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Go語言內存分配器-MSpan

MSpan和FixAlloc一樣,都是內存分配器的基礎工具組件,但和FixAlloc沒太大的交集,各自發揮功效而已。span(MSpan簡稱span)是用來管理一組組page對象,先解釋一下page,page就是一個4k大小的內存塊而已。span就是將這一個個連續的page給管理起來,注意是連續的page,不是東一個西一個的亂擺設的page。為了直觀形象的感受一下span,還是得畫個圖吧,圖形是最好的交流語言。

MSpan結構定義在malloc.h頭文件中,代碼如下:

struct MSpan
{
	MSpan	*next;		// in a span linked list
	MSpan	*prev;		// in a span linked list
	PageID	start;		// starting page number
	uintptr	npages;		// number of pages in span
	MLink	*freelist;	// list of free objects
	uint32	ref;		// number of allocated objects in this span
	int32	sizeclass;	// size class
	uintptr	elemsize;	// computed from sizeclass or from npages
	uint32	state;		// MSpanInUse etc
	int64   unusedsince;	// First time spotted by GC in MSpanFree state
	uintptr npreleased;	// number of pages released to the OS
	byte	*limit;		// end of data in span
	MTypes	types;		// types of allocated objects in this span
};

span結構比較重要的字段,都出現在上面的結構圖中了,當然並不是說其他的字段就不重要了。span的結構中有pre/next兩個指針,一看就能猜到是用來構造雙向鏈表的。沒錯,在實際的使用中,span確實是出現在雙向循環鏈表中。span可能會用在分配小對象(小於等於32k)的過程中,也可能會用於分配大對象(大於32k),在分配不同類型對象的時候,span管理的元數據也大不相同。

npages表示是此span存儲的page的個數(比如:上圖中就畫了3個page),start可以看作一個page指針,指向第一個page,有了第一個page當然就可以算出後面的任何一個page的起始地址了,因為span管理的始終是連續的一組page。這裡需要注意start的類型是PageID,由此可以看出這個start保存的並不是第一個page的起始地址,而是第一個page的id值。這個id值是如何算出來的呢?其實給每個page算一個id,是非常簡單的事情,只要將這個page的的地址除以4096取整(偽代碼:page_addr>>20)即可,當然前提是已經保證好了每個page按4k對齊。是不是覺得很精妙,這樣一來每個page都有一個整數id了,並且任何一個內存地址都可以通過移位算出這個地址屬於哪個page,這個很重要。

start是span最重要的一個字段,它維護好了所有的page。sizeclass如果是0的話,就代表這個span是用來分配大對象的,其他值當然都是分配小對象了。在分配小對象的時候,start字段維護的所有page,最後將會被切分成一個一個的連續內存塊,內存塊大小當然就是小對象的大小,這些切分出來的內存塊將被鏈接成為一個鏈表掛在freelist字段上。分配大對象的時候,freelist就沒什麼用了。

span干的活,也就這麼一點,反正就是管理一組連續的page。內存分配器中的每個page都會屬於一個span,page永遠不會獨立存在。span相關的API有:

// 初始化一個span結構,將分配的page放入到這個span中。
void	runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages);

// 下面這些都是操作span構成的雙向鏈表了。
void	runtime·MSpanList_Init(MSpan *list);
bool	runtime·MSpanList_IsEmpty(MSpan *list);
void	runtime·MSpanList_Insert(MSpan *list, MSpan *span);
void	runtime·MSpanList_Remove(MSpan *span);

span就先寫到這裡,接下來寫mcache, mcentral, mheap等核心組件的時候還會涉及到如何使用span的。

注:本文基於Go1.1.2版本代碼

Copyright © Linux教程網 All Rights Reserved