<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          原來 9 張圖就可以弄懂 Go 內(nèi)存管理

          共 2933字,需瀏覽 6分鐘

           ·

          2020-09-24 13:01

          點擊上方藍(lán)色“Go語言中文網(wǎng)”關(guān)注,回復(fù)「電子書」領(lǐng)全套Go資料

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

          ?? 這篇文章基于 Go 1.13。

          在內(nèi)存從分配到回收的生命周期中,內(nèi)存不再被使用的時候,標(biāo)準(zhǔn)庫會自動執(zhí)行 Go 的內(nèi)存管理。雖然開發(fā)者不必操心這些細(xì)節(jié),但是 Go 語言所做的底層管理經(jīng)過了很好的優(yōu)化,同時有很多有趣的概念。

          堆上的分配

          內(nèi)存管理被設(shè)計為可以在并發(fā)環(huán)境快速執(zhí)行,同時與垃圾收集器集成在了一起。從一個簡單的例子開始:

          package?main

          type?smallStruct?struct?{
          ???a,?b?int64
          ???c,?d?float64
          }

          func?main()?{
          ???smallAllocation()
          }

          //go:noinline
          func?smallAllocation()?*smallStruct?{
          ???return?&smallStruct{}
          }

          注釋 //go:noinline 會禁用內(nèi)聯(lián),以避免內(nèi)聯(lián)通過移除函數(shù)的方式優(yōu)化這段代碼,從而造成最終沒有分配內(nèi)存的情況出現(xiàn)。

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

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

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

          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)

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

          較小內(nèi)存空間的分配策略

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

          用 mcache 分配內(nèi)存

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

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

          span 的大小等級

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

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

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

          span 的中央鏈表

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

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

          從 mcentral 中替換 span

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

          從堆上分配 span

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

          堆由 arena 組成

          較大內(nèi)存空間的分配策略

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

          直接從堆上進(jìn)行大的內(nèi)存空間分配

          全景圖

          現(xiàn)在我們對內(nèi)存分配的時候發(fā)生了什么有了更好的認(rèn)識?,F(xiàn)在將所有的組成部分放在一起來得到完整的圖畫。

          內(nèi)存分配的組成

          靈感來源

          該內(nèi)存分配最初基于 TCMalloc,一個 Google 創(chuàng)建的,并發(fā)環(huán)境優(yōu)化的內(nèi)存分配器。這個 TCMalloc 的文檔[1]值得閱讀;你會發(fā)現(xiàn)上面解釋過的概念。


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

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

          本文由 GCTT[5] 原創(chuàng)編譯,Go 中文網(wǎng)[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 中文網(wǎng): https://studygolang.com/



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包(下圖只是部分),同時還包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。

          關(guān)注公眾號 「polarisxu」,回復(fù) ebook 獲?。贿€可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。


          瀏覽 69
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  永久在线一区二区三区 | 青青草免费在线观看 | 91久久影院 | 亚洲区综合| 亚洲综合在线第一页 |