導讀
本文基於Go原始碼版本1.16、64位Linux平臺、1Page=8KB、本文的記憶體特指虛擬記憶體
今天我們開始進入《Go語言輕鬆進階》系列第二章「記憶體與垃圾回收」第二部分「Go語言記憶體管理」。
關於「記憶體與垃圾回收」章節,會從如下三大部分展開:
讀前知識儲備(已完結)
- Go語言記憶體管理(當前部分)
- Go語言垃圾回收原理(未開始)
第一部分「讀前知識儲備」已經完結,為了更好理解本文大家可以點選歷史連結進行檢視或複習。
目錄
關於講解「Go語言記憶體管理」部分我的思路如下:
- 介紹整體架構
- 介紹架構設計中一個很有意思的地方
- 通過介紹Go記憶體管理中的關鍵結構
mspan
,帶出page
、mspan
、object
、sizeclass
、spanclass
、heaparena
、chunk
的概念 - 接著介紹堆記憶體、棧記憶體的分配
- 回顧和總結
通過這個思路拆解的目錄:
Go記憶體管理架構(本篇內容)
mcache
mcentral
mheap
- 為什麼執行緒快取
mcache
是被邏輯處理器p
持有,而不是系統執行緒m
? Go記憶體管理單元
mspan
page
的概念mspan
的概念object
的概念sizeclass
的概念spanclass
的概念heaparena
的概念chunk
的概念
Go堆記憶體的分配
- 微物件分配
- 小物件分配
- 大物件分配
Go棧記憶體的分配
- 棧記憶體分配時機
- 小於32KB的棧分配
- 大於等於32KB的棧分配
Go記憶體管理架構
Go的記憶體統一由記憶體管理器管理的,Go的記憶體管理器是基於Google自身開源的TCMalloc
記憶體分配器為理念設計和實現的,關於TCMalloc
記憶體分配器的詳細介紹可以檢視之前的文章。
先來簡單回顧下TCMalloc
記憶體分配器的核心設計。
回顧TCMalloc
記憶體分配器
TCMalloc
誕生的背景?
在多核以及超執行緒時代的今天,多執行緒技術已經被廣泛運用到了各個程式語言中。當使用多執行緒技術時,由於多執行緒共享記憶體,執行緒申在請記憶體(虛擬記憶體)時,由於並行問題會產生競爭不安全。
為了保證分配記憶體的過程足夠安全,所以需要在記憶體分配的過程中加鎖,加鎖過程會帶來阻塞影響效能。之後就誕生了TCMalloc
記憶體分配器並被開源。
TCMalloc
如何解決這個問題?
TCMalloc
全稱Thread Cache Memory alloc
執行緒快取記憶體分配器。顧名思義就是給執行緒新增記憶體快取,減少競爭從而提高效能,當執行緒記憶體不足時才會加鎖去共享的記憶體中獲取記憶體。
接著我們來看看TCMalloc
的架構。
TCMalloc
的架構?
TCMalloc
三層邏輯架構
ThreadCache
:執行緒快取CentralFreeList
(CentralCache):中央快取PageHeap
:堆記憶體
TCMalloc
架構上不同的層是如何協作的?
TCMalloc
把申請的記憶體物件按大小分為了兩類:
- 小物件 <= 256 KB
- 大物件 > 256 KB
我們這裡以分配小物件為例,當給小物件分配記憶體時:
- 先去執行緒快取
ThreadCache
中分配 - 當執行緒快取
ThreadCache
的記憶體不足時,從對應SizeClass
的中央快取CentralFreeList
獲取 - 最後,再從對應
SizeClass
的PageHeap
中分配
Go記憶體分配器的邏輯架構
採用了和TCMalloc
記憶體分配器一樣的三層邏輯架構:
mcache
:執行緒快取mcentral
:中央快取mheap
:堆記憶體
<p align="center">
<img src="http://cdn.tigerb.cn/20220405133623.png" style="width:60%">
</p>
實際中央快取central
是一個由136個mcentral
型別元素的陣列構成。
除此之外需要特別注意的地方:mcache
被邏輯處理器p
持有,而並不是被真正的系統執行緒m
持有。(這個設計很有意思,後續會有一篇文章來解釋這個問題)
我們更新下架構圖如下:
「Go記憶體分配器」把申請的記憶體物件按大小分為了三類:
- 微物件 0 < Micro Object < 16B
- 小物件 16B =< Small Object <= 32KB
- 大物件 32KB < Large Object
為了清晰看出這三層的關係,這裡以堆上分配小物件為例:
- 先去執行緒快取
mcache
中分配記憶體 - 找不到時,再去中央快取
central
中分配記憶體 - 最後直接去堆上
mheap
分配一塊記憶體
<p align="center">
<img src="http://cdn.tigerb.cn/20220405224348.png" style="width:80%">
</p>
架構總結
通過以上的分析可以看出Go記憶體分配器的設計和開源TCMalloc
記憶體分配器的理念、思路基本一致。對比圖如下:
最後我們總結下:
Go記憶體分配器採用了和
TCMalloc
一樣的三層架構。邏輯上為:mcache
:執行緒快取mcentral
:中央快取mheap
:堆記憶體
- 執行緒快取
mcache
是被邏輯處理器p
持有,而不是系統執行緒m
檢視《Go語言輕鬆進階》系列更多內容
連結 http://tigerb.cn/go/#/kernal/