GoLang內(nèi)存模型詳細(xì)講解
棧內(nèi)存-協(xié)程棧-調(diào)用棧
為什么go的棧是在堆上?
go 協(xié)程棧的位置: go的協(xié)程棧位于go的堆內(nèi)存,go 的gc 也是對(duì)堆上內(nèi)存進(jìn)行GC, go堆內(nèi)存位于操作系統(tǒng)虛擬內(nèi)存上, 記錄局部變量,傳遞參數(shù)和返回值 ,go 使用的參數(shù)拷貝傳遞,如果傳遞的值比較大 注意傳遞其指針
go 參數(shù)傳遞 使用 值傳遞, 也就是說(shuō)傳遞結(jié)構(gòu)體時(shí)候,拷貝結(jié)構(gòu)體的指針,傳遞結(jié)構(gòu)體指針時(shí)候 拷貝的結(jié)構(gòu)體指針 所以對(duì)于 只讀參數(shù),不進(jìn)行修改,最好傳遞結(jié)構(gòu)體指針
協(xié)程棧的空間不夠大 怎么辦?
本地變量太大,棧幀太多
逃逸分析
不是所有的變量都放在協(xié)程棧上,棧幀回收后,需要繼續(xù)使用的變量,或者 太大的變量,分為指針逃逸,空接口逃逸和大變量逃逸,從棧逃逸分配到堆空間上
指針逃逸 (函數(shù)返回的指針被其他使用)
func a() *int { v :=0 return &v // 導(dǎo)致 局部變量會(huì)分配在堆行 不會(huì)分配棧上 }
空接口逃逸(函數(shù)的參數(shù)是interface{} 函數(shù)的實(shí)參很可能會(huì)逃逸,主要因?yàn)閕nterface{} 類(lèi)型函數(shù)往往會(huì)使用反射)
fmt.Println(i) // 入?yún)儆趇nterface{} 空接口, 是否有反射查看值是什么類(lèi)型 逃逸到堆上
大變量逃逸(過(guò)大變量導(dǎo)致的空間不足,超過(guò)64KB的變量會(huì)逃逸)
// 解決協(xié)程的棧空間不足
調(diào)用棧幀太多(棧擴(kuò)容)
- 解決方式 進(jìn)行棧擴(kuò)容,Go的棧初始空間為2KB
- 在函數(shù)調(diào)用前判斷棧空間morestack
- 早期使用分段棧(go 1.13) 在邏輯上連接,優(yōu)點(diǎn)沒(méi)有空間浪費(fèi),棧指針會(huì)在不連續(xù)的空間跳轉(zhuǎn),后期 連續(xù)棧,缺點(diǎn) 伸縮時(shí)候開(kāi)銷(xiāo)大,擴(kuò)容為原來(lái)2倍, 使用比例不足1/4, 變?yōu)樵瓉?lái)的 1/2
go 堆內(nèi)存
操作系統(tǒng)的虛擬內(nèi)存:操作系統(tǒng)給應(yīng)用提供的虛擬的內(nèi)存的空間,背后也是物理內(nèi)存或者磁盤(pán)
go 使用 heapArena每次申請(qǐng)?zhí)摂M內(nèi)存單元 64MB,所有的heapArena 組成 堆內(nèi)存
線(xiàn)性分配據(jù)或者鏈表分配出現(xiàn)空間碎片,所有g(shù)o 語(yǔ)言中使用分級(jí)分配,避免內(nèi)存的碎片化,每個(gè)內(nèi)存進(jìn)行分級(jí)思想, mspan n內(nèi)存管理單元
按照需求進(jìn)行分級(jí)分配,runtime.sizeclass.go 進(jìn)行分配, 總共有68 個(gè)級(jí)別。
其中 136 個(gè)span , mcentral 屬于鏈接頭,其中 68個(gè)需要GC掃描,其他68個(gè)不需要GC掃描。 mcentral 的屬于中心索引,使用互斥鎖保護(hù),在高并發(fā)的場(chǎng)景下 鎖沖突嚴(yán)重,參考GMP模型,增加線(xiàn)程的本地緩存。
- Go 模仿TCmalloc ,建立自己的堆內(nèi)存架構(gòu)
- 使用heapArena 向操作系統(tǒng)申請(qǐng)內(nèi)存,以mspan 為單位,防止碎片化
- mcentral 是mspan 的中心索引
- 使用mcache 本地緩存 大大降低 鎖競(jìng)爭(zhēng)問(wèn)題
堆如何進(jìn)行分配
- Tiny 微對(duì)象(0,16B)無(wú)指針 – 分配到普通mspan(class 1 - class 67) --將多個(gè)微級(jí)對(duì)象合并成16Byte
- Small 對(duì)象[16B, 32K] – 定制作mspan (class 0)
- Large 大對(duì)象(32KB, +)
- heapArena 不足的化 會(huì)自動(dòng)申請(qǐng)擴(kuò)容
go 語(yǔ)言對(duì)象的垃圾回收
- 標(biāo)記-清除
- 標(biāo)記-整理 (go 語(yǔ)言使用分級(jí)分配 不需要標(biāo)記整理)
- 標(biāo)記-復(fù)制 (只有用內(nèi)存進(jìn)行復(fù)制,但是空間浪費(fèi)非常大,Java的新生代) 總結(jié): Go 堆內(nèi)存的獨(dú)特方式 進(jìn)行標(biāo)記清除掉,如何尋找有用?
- Gc的起點(diǎn): 1. 被棧上的指針引用 2.被全局變量引用 3.被寄存器中指針引用
- Root 節(jié)點(diǎn)進(jìn)行廣度優(yōu)先策略進(jìn)行搜索(可達(dá)性分析標(biāo)記方法)
- 暫停所有其他協(xié)程,進(jìn)行可達(dá)性分析,找到無(wú)引用的GC 屬于串行GC 屬于在OLD version 中
如何減少GC對(duì)性能的分析
如何進(jìn)行并行GC 提升性能?
難點(diǎn)在于如何進(jìn)行標(biāo)記階段,go 語(yǔ)言采用的 三色標(biāo)記方法
- 黑色: 表示已經(jīng)分析掃描,有用
- 灰色: 有用,還沒(méi)進(jìn)行分析掃描 DFS代替隊(duì)列
- 白色: 暫時(shí)無(wú)用
當(dāng)三色標(biāo)記結(jié)束后只有黑色的對(duì)象,下一次開(kāi)啟恢復(fù)成 白色
并發(fā)標(biāo)記的問(wèn)題(刪除)-- 在GC時(shí)候 進(jìn)行對(duì)象的指針的變動(dòng),針對(duì) 并發(fā)標(biāo)記問(wèn)題 使用 Yuasa 刪除屏障, 強(qiáng)制將釋放的C指針變成灰色,避免 在GC過(guò)程中被粗我?標(biāo)記
Yuasa 刪除屏障(s釋放的指針進(jìn)行強(qiáng)制為灰色)
1. 刪除屏障可以杜絕在GC標(biāo)記中刪除的問(wèn)題 ,但是也無(wú)法解決并發(fā)標(biāo)記的插入問(wèn)題
針對(duì)插入屏障 使用 Dijkstra 插入屏障 并發(fā)標(biāo)記過(guò)程中 將C進(jìn)行強(qiáng)制置灰,當(dāng)并發(fā)標(biāo)記過(guò)程,新指針指向新的對(duì)象,新增的依賴(lài)對(duì)象 防止錯(cuò)誤的GC
混合屏障
被刪除的堆對(duì)象標(biāo)記成為灰色
被添加的堆對(duì)象標(biāo)記成為灰色
并發(fā)垃圾回收關(guān)鍵在于標(biāo)記安全,兼顧的安全的效率
GC 優(yōu)化效率
GC觸發(fā)的時(shí)機(jī)
系統(tǒng)定時(shí)觸發(fā)
g0 協(xié)程內(nèi)的sysmon 定時(shí)檢查 ,在2min 內(nèi) forcegcperiod 沒(méi)有過(guò)GC,觸發(fā),謹(jǐn)慎調(diào)整
用戶(hù)顯示觸發(fā)
調(diào)用runtime.gc 并不推薦
申請(qǐng)內(nèi)存觸發(fā)
給申請(qǐng)對(duì)象的時(shí)候伴隨著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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang 使用gorm添加數(shù)據(jù)庫(kù)排他鎖,for update
這篇文章主要介紹了Golang 使用gorm添加數(shù)據(jù)庫(kù)排他鎖,for update,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12Go語(yǔ)言常見(jiàn)錯(cuò)誤之誤用init函數(shù)實(shí)例解析
Go語(yǔ)言中的init函數(shù)為開(kāi)發(fā)者提供了一種在程序正式運(yùn)行前初始化包級(jí)變量的機(jī)制,然而,由于init函數(shù)的特殊性,不當(dāng)?shù)厥褂盟赡芤鹨幌盗袉?wèn)題,本文將深入探討如何有效地使用init函數(shù),列舉常見(jiàn)誤用并提供相應(yīng)的避免策略2024-01-01詳解如何使用unsafe標(biāo)準(zhǔn)庫(kù)突破Golang中的類(lèi)型限制
在使用c語(yǔ)言編程時(shí),常常因?yàn)轭?lèi)型的問(wèn)題大傷腦筋,而,golang提供了一些方式用于喜歡hack的用戶(hù),下面我們就來(lái)講講如何使用unsafe標(biāo)準(zhǔn)庫(kù)突破Golang中的類(lèi)型限制吧2024-03-03golang?MySQL實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)表存儲(chǔ)獲取操作示例
這篇文章主要為大家介紹了golang?MySQL實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)表存儲(chǔ)獲取操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Golang在整潔架構(gòu)基礎(chǔ)上實(shí)現(xiàn)事務(wù)操作
這篇文章在 go-kratos 官方的 layout 項(xiàng)目的整潔架構(gòu)基礎(chǔ)上,實(shí)現(xiàn)優(yōu)雅的數(shù)據(jù)庫(kù)事務(wù)操作,需要的朋友可以參考下2024-08-08prometheus?client_go為應(yīng)用程序自定義監(jiān)控指標(biāo)
這篇文章主要為大家介紹了prometheus?client_go為應(yīng)用程序自定義監(jiān)控指標(biāo)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Go語(yǔ)言框架Beego項(xiàng)目搭建的方法步驟
這篇文章主要介紹了Go語(yǔ)言框架Beego項(xiàng)目搭建的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05Go語(yǔ)言里的結(jié)構(gòu)體文法實(shí)例分析
這篇文章主要介紹了Go語(yǔ)言里的結(jié)構(gòu)體文法,實(shí)例分析了結(jié)構(gòu)體文法的概念及使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02