Golang源碼分析:第二章 內存分配

1.內存分配的基本策略

<1>. 每次從操作系統申請⼀⼤塊內存(⽐如 1MB),以減少系統調⽤。

<2>. 將申請到的⼤塊內存按照特定⼤⼩預先切分成⼩塊,構成鏈表。

<3>. 爲對象分配內存時,只需從⼤⼩合適的鏈表提取⼀個⼩塊即可。

<4>. 回收對象內存時,將該⼩塊內存重新歸還到原鏈表,以便復⽤。

<5>. 如閒置內存過多,則嘗試歸還部分內存給操作系統,降低整體開銷。

2.基本概念

<1>.內存分配器只管理內存塊,並不關⼼對象狀態。且不會主動回收內存,由垃圾回收器在完成清理

操作後,觸發內存分配器回收操作。

<2>.Golang 的內存分配採⽤了 tcmalloc 的成熟架構

<3>.分配器將其管理的內存塊分爲兩種:

• span: 由多個地址連續的頁(page)組成的⼤塊內存。

• object: 將 span 按特定⼤⼩切分成多個⼩塊,每個⼩塊可存儲⼀個對象。

照其⽤途,span ⾯向內部管理,object ⾯向對象分配。

<4>.Golang的內存分配器由三種組件組成。

• cache: 每個運⾏期⼯作線程都會綁定⼀個 cache,⽤於⽆鎖 object 分配。(分配object)

• central: 爲所有 cache 提供切分好的後備 span 資源。(把span切分爲object)

• heap: 管理閒置 span,需要時向操作系統申請新內存。(申請/償還span給操作系統)

<5>.一頁內存8KB;以8字節爲倍數,總共67種不同大小內存;超過32KB被認爲是大對象

<6>.爲⼯作線程私有且不被共享的 cache 是實現⾼性能⽆鎖分配的核⼼,⽽ central 的作⽤是

在多個 cache 間提⾼ object 利⽤率,避免內存浪費。

<7>.假如 cache1 獲取⼀個 span 後,僅使⽤了⼀部分 object,那麼剩餘空間就可能會被浪費。⽽回收操作將該 span 交還給 central 後,cache1已不再持有該 span,那麼該span就會重新分配

<8>.將 span 歸還給 heap,可被其他需求獲取,重新切分,就可以在不同規格 object 需求間平衡。

3.分配流程:

<1>. 計算待分配對象對應規格(size class)。

<2>. 從 cache.alloc 數組找到規格相同的 span。

<3>. 從 span.freelist 鏈表提取可⽤ object。

<4>. 如 span.freelist 爲空,從 central 獲取新 span。

<5>. 如 central.nonempty 爲空,從 heap.free/freelarge 獲取,並切分成 object 鏈表。

<6>. 如 heap 沒有⼤⼩合適的閒置 span,向操作系統申請新內存塊。

4.釋放流程:

<1>. 將標記爲可回收 object 交還給所屬 span.freelist。

<2>. 該 span 被放回 central,可供任意 cache 重新獲取使⽤。

<3>. 如 span 已收回全部 object,則將其交還給 heap,以便重新切分復⽤。

<4>. 定期掃描 heap ⾥長時間閒置的 span,釋放其佔⽤內存。

<5>.⼤對象,直接從 heap 分配和回收。

5.內存對象

//span 對象
type mspan struct {
	next     *mspan    //雙向鏈表
	prev     *mspan    //
	start    pageID    // 起始序號 = (address >> _PageShift)
	npages   uintptr   // 頁數
	freelist gclinkptr // 待分配的object鏈表
}

//三組件
//heap組件
type mheap struct {
	 free [_MaxMHeapList]mspan //頁數在127以內的閒置span鏈表數組
	 freelarge mspan           //頁數大於127(>=1MB)的大span鏈表數組             
	 central [_NumSizeClasses]struct// 每個central對應一種sizeclass
	 {
		mcentral mcentral
	 }
}

//mcentral組件
type mcentral struct {
	sizeclass int32//規格
	nonempty  mspan//鏈表:尚有空閒object的span
	empty     mspan//鏈表:沒有空閒 object,或已被cache取走的span
}

//cache組件
type mcache struct {
	alloc [_NumSizeClasses]*mspan //以sizeclass爲索引管理多個用於分配的span
}

 

6.手工理解圖

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章