日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

原來 9 張圖就可以弄懂 Go 內存管理

Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee F

?? 這篇文章基于 Go 1.13。

在內存從分配到回收的生命周期中,內存不再被使用的時候,標準庫會自動執行 Go 的內存管理。雖然開發者不必操心這些細節,但是 Go 語言所做的底層管理經過了很好的優化,同時有很多有趣的概念。

堆上的分配

內存管理被設計為可以在并發環境快速執行,同時與垃圾收集器集成在了一起。從一個簡單的例子開始:

package main
type smallStruct struct {
   a, b int64
   c, d float64
}func main() {
   smallAllocation()}//go:noinline
func smallAllocation() *smallStruct {
   return &smallStruct{}
}

注釋 //go:noinline 會禁用內聯,以避免內聯通過移除函數的方式優化這段代碼,從而造成最終沒有分配內存的情況出現。

通過運行逃逸分析命令 go tool compile "-m" main.go 可以確認 Go 執行了的分配:

main.go:14:9: &smallStruct literal escapes to heap

借助 go tool compile -S main.go 命令得到這段程序的匯編代碼,可以同樣明確地向我們展示具體的分配細節:

0x001d 00029 (main.go:14)   LEAQ   type."".smallStruct(SB), AX
0x0024 00036 (main.go:14)  PCDATA $0, $0
0x0024 00036 (main.go:14)  MOVQ   AX, (SP)
0x0028 00040 (main.go:14)  CALL   runtime.newobject(SB)

函數 newobject 是用于新對象的分配以及代理 mallocgc 的內置函數,該函數在堆上管理這些內存。在 Go 語言中有兩種策略,一種用于較小的內存空間的分配,而另一種則用于較大的內存空間的分配。

較小內存空間的分配策略

對于小于 32kb 的,較小的內存空間的分配策略,Go 會從被叫做 mcache 的本地緩存中嘗試獲取內存。這個緩存持有一個被叫做 mspan 的內存塊(span ,32kb 大小的內存塊)列表, mspan 包含著可用于分配的內存:

原來 9 張圖就可以弄懂 Go 內存管理

 

用 mcache 分配內存

每個線程 M 被分配一個處理器 P,并且一次最多處理一個 goroutine。在分配內存時,當前的 goroutine 會使用它當前的 P 的本地緩存,在 span 鏈表中尋找第一個可用的空閑對象。使用這種本地緩存不需要鎖操作,從而分配效率更高。

span 鏈表被劃分為 8 字節大小到 32k 字節大小的,約 70 個的大小等級,每個等級可以存儲不同大小的對象。

原來 9 張圖就可以弄懂 Go 內存管理

 

span 的大小等級

每個 span 鏈表會存在兩份:一個鏈表用于不包含指針的對象而另一個用于包含指針的對象。這種區別使得垃圾收集器更加輕松,因為它不必掃描不包含任何指針的 span。

在我們前面的例子中,結構體的大小是 32 字節,因此它會適合于 32 字節的 span :

原來 9 張圖就可以弄懂 Go 內存管理

 

現在,我們可能會好奇,如果在分配期間 span 沒有空閑的插槽會發生什么。Go 維護著每個大小等級的 span 的中央鏈表,該中央鏈表被叫做 mcentral,其中維護著包含空閑對象的 span 和沒有空閑對象的 span :

原來 9 張圖就可以弄懂 Go 內存管理

 

span 的中央鏈表

mcentral 維護著 span 的雙向鏈表;其中每個鏈表節點有著指向前一個 span 和后一個 span 的引用。非空鏈表中的 span 可能包含著一些正在使用的內存,“非空”表示在鏈表中至少有一個空閑的插槽可供分配。當垃圾收集器清理內存時,可能會清理一部分 span,將這部分標記為不再使用,并將其放回非空鏈表。

我們的程序現在可以在沒有插槽的情況下向中央鏈表請求 span :

原來 9 張圖就可以弄懂 Go 內存管理

 

從 mcentral 中替換 span

如果空鏈表中沒有可用的 span,Go 需要為中央鏈表獲取新的 span 。新的 span 會從堆上分配,并鏈接到中央鏈表上:

原來 9 張圖就可以弄懂 Go 內存管理

 

從堆上分配 span

堆會在需要的時候從系統( OS )獲取內存,如果需要更多的內存,堆會分配一個叫做 arena 的大塊內存,在 64 位架構下為 64Mb,在其他架構下大多為 4Mb。arena 同樣適用 span 映射內存。

原來 9 張圖就可以弄懂 Go 內存管理

 

堆由 arena 組成

較大內存空間的分配策略

Go 并不適用本地緩存來管理較大的內存空間分配。對于超過 32kb 的分配,會向上取整到頁的大小,并直接從堆上分配。

原來 9 張圖就可以弄懂 Go 內存管理

 

直接從堆上進行大的內存空間分配

全景圖

現在我們對內存分配的時候發生了什么有了更好的認識。現在將所有的組成部分放在一起來得到完整的圖畫。

原來 9 張圖就可以弄懂 Go 內存管理

 

內存分配的組成

靈感來源

該內存分配最初基于 TCMalloc,一個 google 創建的,并發環境優化的內存分配器。這個 TCMalloc 的文檔[1]值得閱讀;你會發現上面解釋過的概念。


via: https://medium.com/a-journey-with-go/go-memory-management-and-allocation-a7396d430f44

作者:Vincent Blanchon[2]譯者:dust347[3]校對:@unknwon[4]

本文由 GCTT[5] 原創編譯,Go 中文網[6] 榮譽推出

參考資料

[1]

TCMalloc 的文檔: http://goog-perftools.sourceforge.net/doc/tcmalloc.html

[2]

Vincent Blanchon: https://medium.com/@blanchon.vincent

[3]

dust347: https://github.com/dust347

[4]

@unknwon: https://github.com/unknwon

[5]

GCTT: https://github.com/studygolang/GCTT

[6]

Go 中文網: https://studygolang.com/

分享到:
標簽:內存管理 語言
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定