Go記憶體分配和GC的理解

bytecc發表於2022-07-20

程式語言的記憶體分配器一般包含兩種分配方法,一種是線性分配器(Sequential Allocator,Bump Allocator),另一種是空閒連結串列分配器(Free-List Allocator)

線性分配器

只需要在記憶體中維護一個指向記憶體特定位置的指標,指標前面的部分表示已經分配的記憶體區域,指標後面部分是未分配區域,每次申請記憶體,只要移動位置指標即可。

缺點:無法在記憶體釋放時重用記憶體,它們可以透過複製的方式整理存活物件的碎片,將空閒記憶體定期合併,這樣就能利用線性分配器的效率提升記憶體分配器的效能了。

空閒連結串列分配器

將記憶體分割成由 4、8、16、32 位元組的記憶體塊組成的連結串列,申請不同的記憶體大小時,到適合的連結串列上申請記憶體即可。

Go的記憶體分配

TCMalloc是Thread Cache Malloc的簡稱,是Go記憶體管理的起源,Go的記憶體管理是借鑑了TCMalloc。

同一程式的所有執行緒共享相同的記憶體空間,他們申請記憶體時需要加鎖,如果不加鎖就存在同一塊記憶體被2個執行緒同時訪問的問題。

TCMalloc的做法是什麼呢?為每個執行緒預分配一塊快取,執行緒申請小記憶體時,可以從快取分配記憶體,這樣有2個好處:

1、為執行緒預分配快取需要進行1次系統呼叫,後續執行緒申請小記憶體時,從快取分配,都是在使用者態執行,沒有系統呼叫,縮短了記憶體總體的分配和釋放時間,這是快速分配記憶體的第二個層次。
2、多個執行緒同時申請小記憶體時,從各自的快取分配,訪問的是不同的地址空間,無需加鎖,把記憶體併發訪問的粒度進一步降低了,這是快速分配記憶體的第三個層次。

Thread Cache: 每個執行緒各自的Cache,一個Cache包含多個空閒記憶體塊連結串列,不同的連結串列記憶體塊大小也不同,程式根據需要到對應的連結串列申請記憶體。

垃圾回收

所有的 GC 演算法其存在形式可以歸結為追蹤(Tracing)和引用計數(Reference Counting)這兩種形式的混合運用。

  • 追蹤式 GC

    從根物件出發,根據物件之間的引用資訊,一步步推進直到掃描完畢整個堆並確定需要保留的物件,從而回收所有可回收的物件。Go、 Java、V8 對 JavaScript 的實現等均為追蹤式 GC。

  • 引用計數式 GC

    每個物件自身包含一個被引用的計數器,當計數器歸零時自動得到回收。因為此方法缺陷較多,在追求高效能時通常不被應用。Python、Objective-C 等均為引用計數式 GC。

根物件到底是什麼?

根物件在垃圾回收的術語中又叫做根集合,它是垃圾回收器在標記過程時最先檢查的物件,包括:

  1. 全域性變數:程式在編譯期就能確定的那些存在於程式整個生命週期的變數。

  2. 執行棧:每個 goroutine 都包含自己的執行棧,這些執行棧上包含棧上的變數及指向分配的堆記憶體區塊的指標。

  3. 暫存器:暫存器的值可能表示一個指標,參與計算的這些指標可能指向某些賦值器分配的堆記憶體區塊。

追蹤式GC的過程就是mark+sweep

mark+sweep->work….->make+sweep…

標記mark:從根物件開始透過DFS查詢到所有引用的物件,標記為有顏色(說明這些物件都是有用的)

清除sweep:回收未被標記的垃圾物件並將回收的記憶體加入空閒連結串列;

在mark+sweep過程中,使用者程式不能執行,稱為stop the word.造成程式卡頓。為了縮短stw的時間,把mark拆分出來

mark->work->mark->work->mark+sweep->work..

mark一小部分,再切換到使用者程式work,一點點的mark.一直到灰色物件都處理完成。mark階段結束,執行sweep階段。

拆分的mark: 根物件開始查詢所有的引用物件,標記為黑色,把遍歷到的最外層的物件標記為灰色(表示下次mark從那裡開始),其他的物件就是白色物件。

從垃圾回收器的視角來看,三色抽象規定了三種不同型別的物件,並用不同的顏色相稱:

  • 白色物件(可能死亡):未被回收器訪問到的物件。在回收開始階段,所有物件均為白色,當回收結束後,白色物件均不可達。

  • 灰色物件(黑色與白色中間):已被回收器訪問到的物件,但是因為mark中斷,還未完成

  • 黑色物件(確定存活):已被回收器訪問到的物件,其中所有欄位都已被掃描,黑色物件中任何一個指標都不可能直接指向白色物件。

當三色的標記清除的標記階段結束之後,應用程式的堆中就不存在任何的灰色物件,我們只能看到黑色的存活物件以及白色的垃圾物件,垃圾收集器可以回收這些白色的垃圾.
由於加入了第三種顏色來記錄mark過程,所以mark階段可以拆分並且可以和使用者程式併發的執行。大大縮短了STW的時間。

參考連結: github.com/0voice/Introduction-to-...

本作品採用《CC 協議》,轉載必須註明作者和本文連結
用過哪些工具?為啥用這個工具(速度快,支援高併發...)?底層如何實現的?

相關文章