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