go語言有垃圾回收嗎

2022-12-09 22:00:58

go語言有垃圾回收。Go語言自帶垃圾回收機制(GC);GC通過獨立的程序執行,它會搜尋不再使用的變數,並將其釋放。在計算中。記憶體空間包含兩個重要的區域:棧區 (Stack) 和堆區 (Heap);棧區一般儲存了函數呼叫的引數、返回值以及區域性變數,不會產生記憶體碎片,由編譯器管理,無需開發者管理;而堆區會產生記憶體碎片,在Go語言中堆區的物件由記憶體分配器分配並由垃圾收集器回收。

php入門到就業線上直播課:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:

本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。

Go語言自帶垃圾回收機制(GC)。GC 通過獨立的程序執行,它會搜尋不再使用的變數,並將其釋放。需要注意的是,GC 在執行時會佔用機器資源。

Go 語言中的垃圾回收機制 GC 詳解

在電腦科學中,垃圾回收 (Garbage Collection 簡稱 GC) 是一種自動管理記憶體的機制,垃圾回收器會去嘗試回收程式不再使用的物件及佔用的記憶體

程式設計師受益於 GC,無需操心、也不再需要對記憶體進行手動的申請和釋放操作,GC 在程式執行時自動釋放殘留的記憶體
GC 對程式設計師幾乎不可見,僅在程式需要進行特殊優化時,通過提供可調控的 API,對 GC 的執行時機、執行開銷進行把控的時候才得以現身

在計算中,記憶體空間包含兩個重要的區域:棧區 (Stack) 和堆區 (Heap);棧區一般儲存了函數呼叫的引數、返回值以及區域性變數,不會產生記憶體碎片,由編譯器管理,無需開發者管理;而堆區會產生記憶體碎片,在 Go 語言中堆區的物件由記憶體分配器分配並由垃圾收集器回收。【相關推薦:Go視訊教學、】

通常,垃圾回收器的執行過程劃分為兩個半獨立的元件:

  • 使用者程式 (Mutator):使用者態程式碼,對於 GC 而言,使用者態程式碼僅僅只是在修改物件之間的參照關係
  • 收集器 (Colletor):負責執行垃圾回收的程式碼

一、記憶體管理和分配

當記憶體不再使用時,Go 記憶體管理由其標準庫自動執行,即從記憶體分配到 Go 集合。記憶體管理一般包含三個不同的元件,分別是使用者程式 (Mutator)、分配器 (Allocator) 和收集器 (Collector),當使用者程式申請記憶體時,它會通過記憶體分配器申請新記憶體,而分配器會負責從堆中初始化相應的記憶體區域

1.png

1.1 記憶體分配器的分配方法

在程式語言中,記憶體分配器一般有兩種分配方法:

  • 線性分配器 (Sequential Allocator,Bump Allocator)

  • 空閒連結串列分配器 (Free-List Allocator)

線性分配器

線性分配 (Bump Allocator) 是一種高效的記憶體分配方法,但是有較大的侷限性。當使用者使用線性分配器時,只需要在記憶體中維護一個指向記憶體特定位置的指標,如果使用者程式向分配器申請記憶體,分配器只需要檢查剩餘的空閒記憶體、返回分配的記憶體區域並修改指標在記憶體中的位置;

雖然線性分配器有較快的執行速度以及較低的實現複雜度,但線性分配器無法在記憶體釋放後重用記憶體。如下圖,如果已經分配的記憶體被回收,線性分配器無法重新利用紅色的記憶體

2.png

因此線性分配器需要與適合的垃圾回收演演算法配合使用

  • 標記壓縮 (Mark-Compact)

  • 複製回收 (Copying GC)

  • 分代回收 (Generational GC)

以上演演算法可以通過拷貝的方式整理存活物件的碎片,將空閒記憶體定期合併,這樣就能利用線性分配器的效率提升記憶體分配器的效能了

空閒連結串列分配器

空閒連結串列分配器 (Free-List Allocator) 可以重用已經被釋放的記憶體,它在內部會維護一個類似連結串列的資料結構。當使用者程式申請記憶體時,空閒連結串列分配器會依次遍歷空閒的記憶體塊,找到足夠大的記憶體,然後申請新的資源並修改連結串列

3.png

空閒連結串列分配器常見有四種策略:

  • 首次適應 (First-Fit) — 從連結串列頭開始遍歷,選擇第一個大小大於申請記憶體的記憶體塊
  • 迴圈首次適應 (Next-Fit) — 從上次遍歷的結束位置開始遍歷,選擇第一個大小大於申請記憶體的記憶體塊
  • 最優適應 (Best-Fit) — 從連結串列頭遍歷整個連結串列,選擇最合適的記憶體塊
  • 隔離適應 (Segregated-Fit) — 將記憶體分割成多個連結串列,每個連結串列中的記憶體塊大小相同,申請記憶體時先找到滿足條件的連結串列,再從連結串列中選擇合適的記憶體塊

其中第四中策略與 Go 語言中使用的記憶體分配策略相似

4.png

該策略會將記憶體分割成由 4、8、16、32 位元組的記憶體塊組成的連結串列,當我們向記憶體分配器申請 8 位元組的記憶體時,它會在上圖中找到滿足條件的空閒記憶體塊並返回。隔離適應的分配策略減少了需要遍歷的記憶體塊數量,提高了記憶體分配的效率

1.2 Go 中的記憶體分配

一張圖展示記憶體分配組成:

在這裡插入圖片描述

在 Go 語言中,堆上的所有物件都會通過呼叫 函數分配記憶體,該函數會呼叫 分配指定大小的記憶體空間,這也是使用者程式向堆上申請記憶體空間的必經函數

func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
	mp := acquirem()
	mp.mallocing = 1

	c := gomcache()
	var x unsafe.Pointer
	noscan := typ == nil || typ.ptrdata == 0
	if size <= maxSmallSize {
		if noscan && size < maxTinySize {
			// 微物件分配
		} else {
			// 小物件分配
		}
	} else {
		// 大物件分配
	}

	publicationBarrier()
	mp.mallocing = 0
	releasem(mp)

	return x}
登入後複製

從程式碼中可以看出 runtime.mallocgc 根據物件的大小執行不同的分配邏輯,根據物件大小將它們分成微物件、小物件和大物件

  • 微物件 (0, 16B) — 先使用微型分配器,再依次嘗試執行緒快取、中心快取和堆分配記憶體
  • 小物件 [16B, 32KB] — 依次嘗試使用執行緒快取、中心快取和堆分配記憶體
  • 大物件 (32KB, +∞) — 直接在堆上分配記憶體

6.png

小分配

對於小於 32kb 的小分配,Go 會嘗試從 mcache 的本地快取中獲取記憶體,該快取處理一個跨度列表 (32kb 的記憶體塊) mspan

7.png

每個執行緒 M 都分配給一個處理器 P,一次最多處理一個 goroutine。在分配記憶體時,當前的 goroutine 將使用其當前的本地快取 P 來查詢 span 列表中第一個可用的空閒物件

大分配

Go 不使用本地快取管理大型分配。這些大於 32kb 的分配被四捨五入到頁面大小,頁面直接分配到堆中

8.png

二、垃圾回收

在 Go 語言中,垃圾回收器實現的演演算法是一個並行的三色標記和掃描收集器

垃回收器與 Go 程式同時執行,因此需要通過一種演演算法來檢測記憶體中的潛在變化。啟動寫屏障的唯一條件是在短時間內停止程式,即 「Stop the World」

9.png

寫屏障的目的是允許收集器在收集期間保持堆上的資料完整性

2.1 實現原理

Go 語言的垃圾收集可以分成清除終止、標記、標記終止和清除四個不同的階段,其中兩個階段會產生 Stop The World (STW)

10.png

清除終止階段

  • 暫停程式,所有的處理器在這時會進入安全點(Safe point)
  • 如果當前垃圾收集迴圈是強制觸發的,我們還需要處理還未被清理的記憶體管理單元

標記階段 (STW)

  • 將狀態切換至 _GCmark、開啟寫屏障、使用者程式協助(Mutator Assists)並將根物件入隊

  • 恢復執行程式,標記程序和用於協助的使用者程式會開始並行標記記憶體中的物件,寫屏障會將被覆蓋的指標和新指標都標記成灰色,而所有新建立的物件都會被直接標記成黑色

  • 開始掃描根物件,包括所有 Goroutine 的棧、全域性物件以及不在堆中的執行時資料結構,掃描 Goroutine 棧期間會暫停當前處理器

  • 依次處理灰色佇列中的物件,將物件標記成黑色並將它們指向的物件標記成灰色

  • 使用分散式的終止演演算法檢查剩餘的工作,發現標記階段完成後進入標記終止階段

標記終止階段 (STW)

  • 暫停程式、將狀態切換至 _GCmarktermination 並關閉輔助標記的使用者程式
  • 清理處理器上的執行緒快取

清理階段

  • 將狀態切換至 _GCoff 開始清理階段,初始化清理狀態並關閉寫屏障

  • 恢復使用者程式,所有新建立的物件會標記成白色

  • 後臺並行清理所有的記憶體管理單元,當 Goroutine 申請新的記憶體管理單元時就會觸發清理

2.2 三色標記法

三色標記演演算法將程式中的物件分成白色、黑色和灰色三類:

  • 白色物件 — 潛在的垃圾,其記憶體可能會被垃圾收集器回收
  • 黑色物件 — 活躍的物件,包括不存在任何參照外部指標的物件以及從根物件可達的物件
  • 灰色物件 — 活躍的物件,因為存在指向白色物件的外部指標,垃圾收集器會掃描這些物件的子物件

三色標記垃圾收集器的工作原理很簡單,可以將其歸納成以下幾個步驟:

  • 從灰色物件的集合中選擇一個灰色物件並將其標記成黑色

  • 將黑色物件指向的所有物件都標記成灰色,保證該物件和被該物件參照的物件都不會被回收

  • 重複上述兩個步驟直到物件圖中不存在灰色物件

11.png

更多程式設計相關知識,請存取:!!

以上就是go語言有垃圾回收嗎的詳細內容,更多請關注TW511.COM其它相關文章!