GoLang內(nèi)存模型詳細(xì)講解
棧內(nèi)存-協(xié)程棧-調(diào)用棧
為什么go的棧是在堆上?
go 協(xié)程棧的位置: go的協(xié)程棧位于go的堆內(nèi)存,go 的gc 也是對堆上內(nèi)存進(jìn)行GC, go堆內(nèi)存位于操作系統(tǒng)虛擬內(nèi)存上, 記錄局部變量,傳遞參數(shù)和返回值 ,go 使用的參數(shù)拷貝傳遞,如果傳遞的值比較大 注意傳遞其指針

go 參數(shù)傳遞 使用 值傳遞, 也就是說傳遞結(jié)構(gòu)體時候,拷貝結(jié)構(gòu)體的指針,傳遞結(jié)構(gòu)體指針時候 拷貝的結(jié)構(gòu)體指針 所以對于 只讀參數(shù),不進(jìn)行修改,最好傳遞結(jié)構(gòu)體指針
協(xié)程棧的空間不夠大 怎么辦?
本地變量太大,棧幀太多
逃逸分析
不是所有的變量都放在協(xié)程棧上,棧幀回收后,需要繼續(xù)使用的變量,或者 太大的變量,分為指針逃逸,空接口逃逸和大變量逃逸,從棧逃逸分配到堆空間上
指針逃逸 (函數(shù)返回的指針被其他使用)
func a() *int {
v :=0
return &v // 導(dǎo)致 局部變量會分配在堆行 不會分配棧上
}
空接口逃逸(函數(shù)的參數(shù)是interface{} 函數(shù)的實參很可能會逃逸,主要因為interface{} 類型函數(shù)往往會使用反射)
fmt.Println(i) // 入?yún)儆趇nterface{} 空接口, 是否有反射查看值是什么類型 逃逸到堆上
大變量逃逸(過大變量導(dǎo)致的空間不足,超過64KB的變量會逃逸)
// 解決協(xié)程的??臻g不足
調(diào)用棧幀太多(棧擴容)
- 解決方式 進(jìn)行棧擴容,Go的棧初始空間為2KB
- 在函數(shù)調(diào)用前判斷??臻gmorestack
- 早期使用分段棧(go 1.13) 在邏輯上連接,優(yōu)點沒有空間浪費,棧指針會在不連續(xù)的空間跳轉(zhuǎn),后期 連續(xù)棧,缺點 伸縮時候開銷大,擴容為原來2倍, 使用比例不足1/4, 變?yōu)樵瓉淼?1/2
go 堆內(nèi)存
操作系統(tǒng)的虛擬內(nèi)存:操作系統(tǒng)給應(yīng)用提供的虛擬的內(nèi)存的空間,背后也是物理內(nèi)存或者磁盤
go 使用 heapArena每次申請?zhí)摂M內(nèi)存單元 64MB,所有的heapArena 組成 堆內(nèi)存

線性分配據(jù)或者鏈表分配出現(xiàn)空間碎片,所有g(shù)o 語言中使用分級分配,避免內(nèi)存的碎片化,每個內(nèi)存進(jìn)行分級思想, mspan n內(nèi)存管理單元


按照需求進(jìn)行分級分配,runtime.sizeclass.go 進(jìn)行分配, 總共有68 個級別。

其中 136 個span , mcentral 屬于鏈接頭,其中 68個需要GC掃描,其他68個不需要GC掃描。 mcentral 的屬于中心索引,使用互斥鎖保護(hù),在高并發(fā)的場景下 鎖沖突嚴(yán)重,參考GMP模型,增加線程的本地緩存。
- Go 模仿TCmalloc ,建立自己的堆內(nèi)存架構(gòu)
- 使用heapArena 向操作系統(tǒng)申請內(nèi)存,以mspan 為單位,防止碎片化
- mcentral 是mspan 的中心索引
- 使用mcache 本地緩存 大大降低 鎖競爭問題
堆如何進(jìn)行分配
- Tiny 微對象(0,16B)無指針 – 分配到普通mspan(class 1 - class 67) --將多個微級對象合并成16Byte
- Small 對象[16B, 32K] – 定制作mspan (class 0)
- Large 大對象(32KB, +)
- heapArena 不足的化 會自動申請擴容
go 語言對象的垃圾回收
- 標(biāo)記-清除
- 標(biāo)記-整理 (go 語言使用分級分配 不需要標(biāo)記整理)
- 標(biāo)記-復(fù)制 (只有用內(nèi)存進(jìn)行復(fù)制,但是空間浪費非常大,Java的新生代) 總結(jié): Go 堆內(nèi)存的獨特方式 進(jìn)行標(biāo)記清除掉,如何尋找有用?
- Gc的起點: 1. 被棧上的指針引用 2.被全局變量引用 3.被寄存器中指針引用
- Root 節(jié)點進(jìn)行廣度優(yōu)先策略進(jìn)行搜索(可達(dá)性分析標(biāo)記方法)
- 暫停所有其他協(xié)程,進(jìn)行可達(dá)性分析,找到無引用的GC 屬于串行GC 屬于在OLD version 中
如何減少GC對性能的分析
如何進(jìn)行并行GC 提升性能?
難點在于如何進(jìn)行標(biāo)記階段,go 語言采用的 三色標(biāo)記方法
- 黑色: 表示已經(jīng)分析掃描,有用
- 灰色: 有用,還沒進(jìn)行分析掃描 DFS代替隊列
- 白色: 暫時無用

當(dāng)三色標(biāo)記結(jié)束后只有黑色的對象,下一次開啟恢復(fù)成 白色
并發(fā)標(biāo)記的問題(刪除)-- 在GC時候 進(jìn)行對象的指針的變動,針對 并發(fā)標(biāo)記問題 使用 Yuasa 刪除屏障, 強制將釋放的C指針變成灰色,避免 在GC過程中被粗我?標(biāo)記

Yuasa 刪除屏障(s釋放的指針進(jìn)行強制為灰色)

1. 刪除屏障可以杜絕在GC標(biāo)記中刪除的問題 ,但是也無法解決并發(fā)標(biāo)記的插入問題

針對插入屏障 使用 Dijkstra 插入屏障 并發(fā)標(biāo)記過程中 將C進(jìn)行強制置灰,當(dāng)并發(fā)標(biāo)記過程,新指針指向新的對象,新增的依賴對象 防止錯誤的GC
混合屏障
被刪除的堆對象標(biāo)記成為灰色
被添加的堆對象標(biāo)記成為灰色
并發(fā)垃圾回收關(guān)鍵在于標(biāo)記安全,兼顧的安全的效率
GC 優(yōu)化效率
GC觸發(fā)的時機
系統(tǒng)定時觸發(fā)
g0 協(xié)程內(nèi)的sysmon 定時檢查 ,在2min 內(nèi) forcegcperiod 沒有過GC,觸發(fā),謹(jǐn)慎調(diào)整
用戶顯示觸發(fā)
調(diào)用runtime.gc 并不推薦
申請內(nèi)存觸發(fā)
給申請對象的時候伴隨著GC
GC優(yōu)化原則
盡量少在堆上產(chǎn)生垃圾
內(nèi)存池化(channel 中 環(huán)形池)
減少逃逸 (fmt 包, 返回了指針不是拷貝)
使用空結(jié)構(gòu)體 (不占用空結(jié)構(gòu)體,使用channel 傳遞空結(jié)構(gòu)體)
使用如下的方式 查看內(nèi)存
$env:GODEBUG="gctrace=1"
到此這篇關(guān)于GoLang內(nèi)存模型詳細(xì)講解的文章就介紹到這了,更多相關(guān)Go內(nèi)存模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang 使用gorm添加數(shù)據(jù)庫排他鎖,for update
這篇文章主要介紹了Golang 使用gorm添加數(shù)據(jù)庫排他鎖,for update,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12
詳解如何使用unsafe標(biāo)準(zhǔn)庫突破Golang中的類型限制
在使用c語言編程時,常常因為類型的問題大傷腦筋,而,golang提供了一些方式用于喜歡hack的用戶,下面我們就來講講如何使用unsafe標(biāo)準(zhǔn)庫突破Golang中的類型限制吧2024-03-03
golang?MySQL實現(xiàn)對數(shù)據(jù)庫表存儲獲取操作示例
這篇文章主要為大家介紹了golang?MySQL實現(xiàn)對數(shù)據(jù)庫表存儲獲取操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
Golang在整潔架構(gòu)基礎(chǔ)上實現(xiàn)事務(wù)操作
這篇文章在 go-kratos 官方的 layout 項目的整潔架構(gòu)基礎(chǔ)上,實現(xiàn)優(yōu)雅的數(shù)據(jù)庫事務(wù)操作,需要的朋友可以參考下2024-08-08
prometheus?client_go為應(yīng)用程序自定義監(jiān)控指標(biāo)
這篇文章主要為大家介紹了prometheus?client_go為應(yīng)用程序自定義監(jiān)控指標(biāo)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02

