go語言有垃圾回收。Go語言自帶垃圾回收機制(GC);GC通過獨立的程序執行,它會搜尋不再使用的變數,並將其釋放。在計算中。記憶體空間包含兩個重要的區域:棧區 (Stack) 和堆區 (Heap);棧區一般儲存了函數呼叫的引數、返回值以及區域性變數,不會產生記憶體碎片,由編譯器管理,無需開發者管理;而堆區會產生記憶體碎片,在Go語言中堆區的物件由記憶體分配器分配並由垃圾收集器回收。
php入門到就業線上直播課:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:
本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。
Go語言自帶垃圾回收機制(GC)。GC 通過獨立的程序執行,它會搜尋不再使用的變數,並將其釋放。需要注意的是,GC 在執行時會佔用機器資源。
在電腦科學中,垃圾回收 (Garbage Collection 簡稱 GC) 是一種自動管理記憶體的機制,垃圾回收器會去嘗試回收程式不再使用的物件及佔用的記憶體
程式設計師受益於 GC,無需操心、也不再需要對記憶體進行手動的申請和釋放操作,GC 在程式執行時自動釋放殘留的記憶體
GC 對程式設計師幾乎不可見,僅在程式需要進行特殊優化時,通過提供可調控的 API,對 GC 的執行時機、執行開銷進行把控的時候才得以現身
在計算中,記憶體空間包含兩個重要的區域:棧區 (Stack) 和堆區 (Heap);棧區一般儲存了函數呼叫的引數、返回值以及區域性變數,不會產生記憶體碎片,由編譯器管理,無需開發者管理;而堆區會產生記憶體碎片,在 Go 語言中堆區的物件由記憶體分配器分配並由垃圾收集器回收。【相關推薦:Go視訊教學、】
通常,垃圾回收器的執行過程劃分為兩個半獨立的元件:
當記憶體不再使用時,Go 記憶體管理由其標準庫自動執行,即從記憶體分配到 Go 集合。記憶體管理一般包含三個不同的元件,分別是使用者程式 (Mutator)、分配器 (Allocator) 和收集器 (Collector),當使用者程式申請記憶體時,它會通過記憶體分配器申請新記憶體,而分配器會負責從堆中初始化相應的記憶體區域
在程式語言中,記憶體分配器一般有兩種分配方法:
線性分配器 (Sequential Allocator,Bump Allocator)
空閒連結串列分配器 (Free-List Allocator)
線性分配器
線性分配 (Bump Allocator) 是一種高效的記憶體分配方法,但是有較大的侷限性。當使用者使用線性分配器時,只需要在記憶體中維護一個指向記憶體特定位置的指標,如果使用者程式向分配器申請記憶體,分配器只需要檢查剩餘的空閒記憶體、返回分配的記憶體區域並修改指標在記憶體中的位置;
雖然線性分配器有較快的執行速度以及較低的實現複雜度,但線性分配器無法在記憶體釋放後重用記憶體。如下圖,如果已經分配的記憶體被回收,線性分配器無法重新利用紅色的記憶體
因此線性分配器需要與適合的垃圾回收演演算法配合使用
標記壓縮 (Mark-Compact)
複製回收 (Copying GC)
分代回收 (Generational GC)
以上演演算法可以通過拷貝的方式整理存活物件的碎片,將空閒記憶體定期合併,這樣就能利用線性分配器的效率提升記憶體分配器的效能了
空閒連結串列分配器
空閒連結串列分配器 (Free-List Allocator) 可以重用已經被釋放的記憶體,它在內部會維護一個類似連結串列的資料結構。當使用者程式申請記憶體時,空閒連結串列分配器會依次遍歷空閒的記憶體塊,找到足夠大的記憶體,然後申請新的資源並修改連結串列
空閒連結串列分配器常見有四種策略:
其中第四中策略與 Go 語言中使用的記憶體分配策略相似
該策略會將記憶體分割成由 4、8、16、32 位元組的記憶體塊組成的連結串列,當我們向記憶體分配器申請 8 位元組的記憶體時,它會在上圖中找到滿足條件的空閒記憶體塊並返回。隔離適應的分配策略減少了需要遍歷的記憶體塊數量,提高了記憶體分配的效率
一張圖展示記憶體分配組成:
在 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, +∞)
— 直接在堆上分配記憶體小分配
對於小於 32kb 的小分配,Go 會嘗試從 mcache
的本地快取中獲取記憶體,該快取處理一個跨度列表 (32kb 的記憶體塊) mspan
每個執行緒 M 都分配給一個處理器 P,一次最多處理一個 goroutine
。在分配記憶體時,當前的 goroutine
將使用其當前的本地快取 P 來查詢 span
列表中第一個可用的空閒物件
大分配
Go 不使用本地快取管理大型分配。這些大於 32kb 的分配被四捨五入到頁面大小,頁面直接分配到堆中
在 Go 語言中,垃圾回收器實現的演演算法是一個並行的三色標記和掃描收集器
垃回收器與 Go 程式同時執行,因此需要通過一種演演算法來檢測記憶體中的潛在變化。啟動寫屏障的唯一條件是在短時間內停止程式,即 「Stop the World」
寫屏障的目的是允許收集器在收集期間保持堆上的資料完整性
Go 語言的垃圾收集可以分成清除終止、標記、標記終止和清除四個不同的階段,其中兩個階段會產生 Stop The World (STW)
清除終止階段
標記階段 (STW)
將狀態切換至 _GCmark
、開啟寫屏障、使用者程式協助(Mutator Assists)並將根物件入隊
恢復執行程式,標記程序和用於協助的使用者程式會開始並行標記記憶體中的物件,寫屏障會將被覆蓋的指標和新指標都標記成灰色,而所有新建立的物件都會被直接標記成黑色
開始掃描根物件,包括所有 Goroutine 的棧、全域性物件以及不在堆中的執行時資料結構,掃描 Goroutine 棧期間會暫停當前處理器
依次處理灰色佇列中的物件,將物件標記成黑色並將它們指向的物件標記成灰色
使用分散式的終止演演算法檢查剩餘的工作,發現標記階段完成後進入標記終止階段
標記終止階段 (STW)
_GCmarktermination
並關閉輔助標記的使用者程式清理階段
將狀態切換至 _GCoff
開始清理階段,初始化清理狀態並關閉寫屏障
恢復使用者程式,所有新建立的物件會標記成白色
後臺並行清理所有的記憶體管理單元,當 Goroutine 申請新的記憶體管理單元時就會觸發清理
三色標記演演算法將程式中的物件分成白色、黑色和灰色三類:
三色標記垃圾收集器的工作原理很簡單,可以將其歸納成以下幾個步驟:
從灰色物件的集合中選擇一個灰色物件並將其標記成黑色
將黑色物件指向的所有物件都標記成灰色,保證該物件和被該物件參照的物件都不會被回收
重複上述兩個步驟直到物件圖中不存在灰色物件
更多程式設計相關知識,請存取:!!
以上就是go語言有垃圾回收嗎的詳細內容,更多請關注TW511.COM其它相關文章!