golang 垃圾回收器如何標記記憶體?
本文基於 Go 1.13。這裡討論的關於記憶體管理的概念在我的文章Go:記憶體管理和分配 中有解釋
Go 垃圾回收器負責回收不再使用的記憶體。實現的演算法是一個並行的三色標記掃描採集器。在本文中,我們將詳細瞭解標記階段,以及不同顏色的用法。
您可以在 kenfox 的視覺化垃圾回收演算法 中找到關於不同型別垃圾回收器的更多資訊。
標記階段
此階段執行記憶體掃描,以瞭解程式碼仍在使用哪些塊,以及應該回收哪些塊。
但是,由於垃圾回收器可以與我們的 Go 程式同時執行,因此它需要一種在掃描時檢測記憶體中潛在變化的方法。為了解決這個潛在的問題,實現了一個寫屏障演算法,允許 Go 跟蹤任何指標的變化。啟用寫屏障的唯一條件是短時間停止程式,也稱為 “STW”:
在程式開始時,Go 還會為每個處理器啟動一個標記輔助程式,以幫助標記記憶體。
然後,一旦根節點被排隊等待處理,標記階段就可以開始遍歷記憶體併為其著色。
現在讓我們以一個簡單的程式為例,該程式允許我們遵循標記階段所做的步驟
Type struct1 struct {
a, b int64
c, d float64
e *struct2
}
type struct2 struct {
f, g int64
h, i float64
}
func main() {
s1 := allocStruct1()
s2 := allocStruct2()
func () {
_ = allocStruct2()
}()
runtime.GC()
fmt.Printf("s1 = %X, s2 = %X\n", &s1, &s2)
}
//go:noinline
func allocStruct1() *struct1 {
return &struct1{
e: allocStruct2(),
}
}
//go:noinline
func allocStruct2() *struct2 {
return &struct2{}
}
由於 struct subStruct 不包含任何指標,因此它儲存在一個專用於物件的範圍中,而不引用其他物件:
這使得垃圾回收器的工作更容易,因為它在標記記憶體時不必掃描這個範圍。
一旦分配完成,我們的程式就會強制垃圾回收器執行一個週期。以下是工作流程:
垃圾回收器從堆疊開始標記,然後跟著指標遞迴遍歷記憶體。直到物件都被標記時停止掃描。然而,這個過程不是在同一個 goroutine 中完成的;每個指標都在工作池中排隊。然後 ,後臺的標記執行緒發現之前的出列佇列是來自該工作池,掃描物件,然後將在其中找到的指標加入佇列:
著色!
後臺執行緒現在需要一種方法來跟蹤哪些記憶體有沒有被掃描。垃圾回收器使用三色演算法,其工作原理如下:
所有物件一開始都被認為是白色的
根物件(堆疊、堆、全域性變數)將以灰色顯示
完成此主要步驟後,垃圾回收器將:
選擇一個灰色的物件,把它塗成黑色
遵循此物件的所有指標並將所有引用的物件塗成灰色
然後,它將重複這兩個步驟,直到沒有更多的物件要著色。從這一點來看,物件不是黑色就是白色。白色集合表示未被任何其他物件引用且準備好回收的物件。
下面是使用上一個示例對其進行的表示:
作為第一種狀態,所有物件都被視為白色。然後,物件被遍歷,可到達的物件將變為灰色。如果物件位於標記為 “無掃描” 的範圍內,則可以將其繪製為黑色,因為不需要對其進行掃描:
灰色物件現在入隊等待掃描並變黑:
在沒有更多的物件要處理之前,入隊的物件也會發生同樣的情況:
在程式結束時,黑色物件是記憶體中正在使用的物件,而白色物件是要回收的物件。如我們所見,由於 struct2 的例項是在匿名函式中建立的,並且無法從堆疊訪問,因此它保持為白色,可以清除。
由於每個跨度中有一個名為 gcmarkBits 的點陣圖屬性,顏色在內部實現,該屬性跟蹤掃描,並將相應的位設定為 1:
正如我們所見,黑色和灰色的工作原理是一樣的。這一過程的不同之處在於,當黑色物件結束掃描鏈時,灰色物件排隊等待掃描。
垃圾回收器最終會 stops the world,將每個寫屏障上所做的更改重新整理到工作池,並執行剩餘的標記。
您可以在我的文章Go:垃圾回收器如何監視您的應用程式 中找到有關併發程式和垃圾回收器中標記階段的更多詳細資訊
執行時分析器
Go 提供的工具允許我們視覺化所有這些步驟,並在程式中檢視垃圾回收器的影響。在啟用跟蹤的情況下執行我們的程式碼提供了前面步驟的一個圖片。以下是 traces:
標記執行緒的生命週期也可以在 goroutine 級別的 tracer 中視覺化。下面是 goroutine#33 的示例,它在開始標記記憶體之前先在後臺等待。
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- JVM垃圾回收器、記憶體分配與回收策略JVM記憶體
- JavaScript 記憶體管理及垃圾回收JavaScript記憶體
- JVM記憶體管理和垃圾回收JVM記憶體
- Node - 記憶體管理和垃圾回收記憶體
- Node記憶體限制和垃圾回收記憶體
- Java記憶體管理 -JVM 垃圾回收Java記憶體JVM
- 探索JVM的垃圾回收(堆記憶體)JVM記憶體
- JVM垃圾回收和記憶體分配策略JVM記憶體
- PHP 垃圾回收與記憶體管理指引PHP記憶體
- C# 垃圾回收釋放記憶體C#記憶體
- jvm:記憶體模型、記憶體分配及GC垃圾回收機制JVM記憶體模型GC
- Java記憶體模型,垃圾回收機制,常用記憶體命令及工具Java記憶體模型
- Golang 垃圾回收-三色標記清除演算法Golang演算法
- 【JVM之記憶體與垃圾回收篇】堆JVM記憶體
- 淺談JVM記憶體分配與垃圾回收JVM記憶體
- 垃圾回收與記憶體分配——總結篇記憶體
- 【JVM之記憶體與垃圾回收篇】虛擬機器棧JVM記憶體虛擬機
- Java進階10 記憶體管理與垃圾回收Java記憶體
- Javascrip高程中的垃圾記憶體回收制(6)Java記憶體
- javascript的垃圾回收機制和記憶體管理JavaScript記憶體
- JavaScript中的垃圾回收和記憶體洩漏JavaScript記憶體
- JVM記憶體分配策略,及垃圾回收演算法JVM記憶體演算法
- V8記憶體管理及垃圾回收機制記憶體
- NodeJS V8引擎的記憶體和垃圾回收器(GC)NodeJS記憶體GC
- 深入理解JVM記憶體回收機制(不包含垃圾收集器)JVM記憶體
- 垃圾收集器與記憶體分配策略_記憶體分配策略記憶體
- [譯] 通過垃圾回收機制理解 JavaScript 記憶體管理JavaScript記憶體
- JVM垃圾回收之三色標記JVM
- redis的記憶體滿了之後,redis如何回收記憶體嗎Redis記憶體
- golang垃圾回收Golang
- 【JVM之記憶體與垃圾回收篇】物件例項化記憶體佈局與訪問定位JVM記憶體物件
- 記憶體回收介紹記憶體
- Go 高效能系列教程之五:記憶體和垃圾回收Go記憶體
- 從JAVA記憶體到垃圾回收,帶你深入理解JVMJava記憶體JVM
- js垃圾回收機制和引起記憶體洩漏的操作JS記憶體
- Golang 共享記憶體Golang記憶體
- 垃圾收集器與記憶體分配策略記憶體
- JVM——垃圾收集器與記憶體分配JVM記憶體