【go】什么是Go語言中的GC,作用是什么?調優,sync.Pool優化,逃逸分析演示

Go 語言中的 GC 簡介與調優建議

Go語言GC工作原理

對于 Go 而言,Go 的 GC 目前使用的是無分代(對象沒有代際之分)、不整理(回收過程中不對對象進行移動與整理)、并發(與用戶代碼并發執行)的三色標記清掃算法。
  • 分代:指的是將對象按照存活時間分為新生代(存活時間小)老年代(存活時間久),以及永遠不參與回收的永久代
    由此將主要回收目標放到新生代上(存活時間短,更傾向于被回收)。但是go的編譯器會通過逃逸分析,將大部分新生代放到棧上(棧會直接回收),而需要長期存儲的放在需要進行垃圾回收的堆中
    即需要回收的新生代會隨著goroutine棧的銷毀而回收,不需要gc的參與,所以go采用不分代。

  • 整理:指的是在對象移動時將他們排列整齊,,意在解決內存碎片問題,“允許”順序內存分配。但是go運行時分配的算法是tcmalloc,基本上沒有碎片問題,并且順序內存存儲在多線程場景下不適用,所以go采用不整理。

二、工作流程(三色標記法)
階段工作內容是否STW
標記準備暫停應用,啟用寫屏障,初始化掃描根對象(棧/全局變量等)
并發標記后臺掃描對象圖,通過寫屏障跟蹤并發修改
標記終止完成剩余標記,關閉寫屏障
并發清除回收白色對象內存
  • 白色對象(可能死亡)被回收器訪問到的對象。在回收開始階段,所有對象均為白色,當回收結束后,白色對象均不可達。
  • 灰色對象(波面)被回收器訪問到的對象,但回收器需要對其中的一個或多個指針進行掃描,因為他們可能還指向白色對象。
  • 黑色對象(確定存活)被回收器訪問到的對象,其中所有字段都已被掃描,黑色對象中任何一個指針都不可能直接指向白色對象。
  • STW 是 “Stop The World” 的縮寫,有時也可以理解為 “Start The World”,但我們通常說的 STW,指的是 “從程序暫停(Stop)到恢復(Start)之間的這段時間”。

垃圾回收開始,全為白對象,標記過程開始,白對象逐漸開始白->灰,當灰色對象所有子節點都掃描完后,灰->黑。整個堆遍歷完之后,只剩下黑和白,清理白。

為什么會發生STW?因為垃圾回收時要清理不用的內存,如果對象還在修改,回收時可能出錯,所以要,暫停用戶代碼,專心回收。

三、Go語言中的根對象組成

根對象類型具體內容生命周期掃描頻率
全局變量包級變量(var globalVar *T)、常量等程序整個生命周期每次GC標記階段
Goroutine棧每個goroutine棧幀中的局部變量、函數參數、返回值等Goroutine存活期間每次GC標記階段
寄存器CPU寄存器中存儲的臨時指針(如正在參與計算的引用)執行指令期間掃描棧時同步捕獲
運行時數據結構runtime.sched管理的全局隊列、finalizer隊列、sync.Pool緩存對象等運行時管理每次GC標記階段

四、常見內存回收算法對比
算法實現方式優點缺點Go中的應用場景
標記-清除標記存活對象后直接回收內存利用率高產生內存碎片大對象堆內存回收
復制算法存活對象復制到新空間無碎片、訪問局部性好浪費50%空間小對象MCache分配
標記-整理移動存活對象到連續空間無碎片、空間緊湊移動成本高未直接使用

二、GC 調優建議

  1. 減少內存分配次數

    • 盡量重用對象,避免頻繁創建和銷毀;
    • 處理字符串拼接時推薦使用 strings.Builder 替代 +,可減少中間對象生成;
    • 減少 slice/map 的擴容行為,適當預估容量。

    示例對比:

    // 不推薦:頻繁分配新字符串
    s := ""
    for _, str := range list {s += str
    }// 推薦:使用 strings.Builder
    var builder strings.Builder
    for _, str := range list {builder.WriteString(str)
    }
    
  2. 合并小對象,使用對象池

    • 多個小對象可以設計為一個結構體批量分配,減少單獨分配;
    • 對于高頻使用的臨時對象,推薦使用 sync.Pool 復用,避免反復分配和回收。
    var bufPool = sync.Pool{New: func() any {return make([]byte, 1024)},
    }func handler() {buf := bufPool.Get().([]byte)defer bufPool.Put(buf)// 使用 buf ...
    }
    
  3. 調整 GC 觸發頻率

    Go GC 的觸發頻率由一個稱為 GOGC(GC Percent) 的參數控制,表示堆增長百分比。

    • 默認值是 100,表示堆增長 100% 后觸發一次 GC;
    • 增大該值可以減少 GC 次數,提升性能,但會占用更多內存
    • 可以通過代碼動態設置觸發比例:
    import "runtime/debug"func init() {debug.SetGCPercent(200) // 增加 GC 觸發閾值,適用于內存充足場景
    }
    

三、小結

優化方向方法舉例
減少分配重用對象、使用 strings.Builder、減少 slice/map 擴容
合并對象多字段合并為結構體、避免小對象碎片化
對象復用使用 sync.Pool 作為臨時對象池
調整頻率通過 debug.SetGCPercent() 或環境變量 GOGC 設置觸發頻率
分析工具使用 GODEBUG=gctrace=1pprof 分析 GC 活動和內存使用情況

非常好!下面我帶你一步步完成一個使用 sync.Pool 的優化示例,并教你如何使用 Go 的 逃逸分析工具 來判斷優化效果。


示例背景:構建 JSON 字符串,頻繁分配 []byte

寫一個模擬處理請求的函數,返回 JSON 格式的響應字符串。每次處理都分配一個 []byte 緩沖區。


原始版本(每次都分配新的 []byte):

package mainimport ("fmt"
)func handleRequest() {buf := make([]byte, 0, 1024)buf = append(buf, `{"code":200,"message":"ok"}`...)fmt.Println(string(buf))
}func main() {for i := 0; i < 1000; i++ {handleRequest()}
}

每次 make([]byte, 0, 1024) 都會分配新內存,GC 負擔重。


優化版本:使用 sync.Pool 復用 []byte

package mainimport ("fmt""sync"
)var bufPool = sync.Pool{New: func() any {// 初始化容量為 1024 的 byte slicereturn make([]byte, 0, 1024)},
}func handleRequest() {buf := bufPool.Get().([]byte)// 重置長度為 0,保留容量buf = buf[:0]buf = append(buf, `{"code":200,"message":"ok"}`...)fmt.Println(string(buf))bufPool.Put(buf)
}func main() {for i := 0; i < 1000; i++ {handleRequest()}
}

通過 sync.Pool,我們復用了 []byte,避免了頻繁內存分配,GC 壓力大幅減輕。


如何做逃逸分析

Go 編譯器可以告訴你變量是否逃逸到堆上。命令如下:

go build -gcflags="-m" main.go

你會看到類似輸出(原始版本中):

./main.go:8:6: moved to heap: buf

表示 buf 逃逸到了堆 → 會被 GC 回收

而優化后版本中(使用 sync.Pool)你應該看到:

./main.go:15:6: buf does not escape

說明變量被控制在了棧上,不會被 GC 管理,性能更好。


總結

技術點說明
sync.Pool用于復用臨時對象,減少 GC 壓力
逃逸分析工具go build -gcflags="-m" 可查看變量是否逃逸到堆
優化場景高頻創建/銷毀的臨時對象,如 []bytestrings.Builder
注意事項使用 sync.Pool 后的對象必須手動重置狀態,避免臟數據

https://github.com/0voice

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/80105.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/80105.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/80105.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【unity實戰】Animator啟用root motion根運動動畫,實現完美的動畫動作匹配

文章目錄 前言1、動畫分類2、如何使用根位移動畫&#xff1f; 一、根位移動畫的具體使用1、導入人形模型2、導入動畫3、配置動畫參數4、配置角色Animator動畫狀態機5、使用代碼控制人物前進后退 二、問題分析三、Humanoid動畫中的Root Motion機制及相關配置1、Humanoid動畫中的…

中間件--ClickHouse-10--海量數據存儲如何抉擇ClickHouse和ES?

在Mysql數據存儲或性能瓶頸時&#xff0c;采用冷熱數據分離的方式通常是一種選擇。ClickHouse和Elasticsearch&#xff08;ES&#xff09;是兩個常用的組件&#xff0c;但具體使用哪種組件取決于冷數據的存儲目的、查詢模式和業務需求等方面。 1、核心對比 &#xff08;1&…

服務器運維:服務器流量的二八法則是什么意思?

文章目錄 用戶行為角度時間分布角度應用場景角度 服務器流量的二八法則&#xff0c;又稱 80/20 法則&#xff0c;源自意大利經濟學家帕累托提出的帕累托法則&#xff0c;該法則指出在很多情況下&#xff0c;80% 的結果是由 20% 的因素所決定的。在服務器流量領域&#xff0c;二…

springboot對接豆包大模型

文檔地址: 豆包大模型-火山引擎 模型廣場地址: 賬號登錄-火山引擎 首先來到模型廣場&#xff0c;選取你需要的模型,我這邊要做圖片理解的應用&#xff0c;所以選用了Doubao-1.5.vision-pro. 點立即體驗&#xff0c;進入一個新的頁面&#xff0c;可以上傳圖片&#xff0c;然后…

數據通信學習筆記之OSPF其他內容3

對發送的 LSA 進行過濾 當兩臺路由器之間存在多條鏈路時&#xff0c;可以在某些鏈路上通過對發送的 LSA 進行過濾&#xff0c;減少不必要的重傳&#xff0c;節省帶寬資源。 通過對 OSPF 接口出方向的 LSA 進行過濾可以不向鄰居發送無用的 LSA&#xff0c;從而減少鄰居 LSDB 的…

智能安全用電系統預防電氣線路老化、線路或設備絕緣故障

智能安全用電系統預防電氣線路老化、線路或設備絕緣故障 智能安全用電系統&#xff0c;猶如一位忠實而敏銳的衛士&#xff0c;主要針對低壓供電網中一系列潛在的危險狀況進行了全方位且行之有效的預防和保護。 智能安全用電系統在低壓供電網這個復雜的體系中&#xff0c;電氣線…

使用Intel Advisor工具分析程序

使用Intel Advisor工具分析程序 Intel Advisor是一款性能分析工具&#xff0c;主要用于識別代碼中的向量化機會、線程化和內存訪問模式等問題。以下是使用Intel Advisor分析程序的基本步驟&#xff1a; 安裝與準備 從Intel官網下載并安裝Intel Advisor&#xff08;通常作為I…

【UniApp】Vue2 scss 預編譯器默認已由 node-sass 更換為 dart-sass

從 HBuilderX 4.56 &#xff0c;vue2 項目也將默認使用 dart-sass 預編譯器。 vue2開發者sass預處理注意&#xff1a; sass的預處理器&#xff0c;早年使用node-sass&#xff0c;也就是vue2最初默認的編譯器。 sass官方推出了dart-sass來替代。node-sass已經停維很久了。 另…

智慧能源安全新紀元:當能源監測遇上視頻聯網的無限可能

引言&#xff1a;在數字化浪潮席卷全球的今天&#xff0c;能源安全已成為國家安全戰略的重要組成部分。如何構建更加智能、高效的能源安全保障體系&#xff1f;能源安全監測平臺與視頻監控聯網平臺的深度融合&#xff0c;正為我們開啟一扇通向未來能源管理新世界的大門。這種創…

C++游戲服務器開發之⑦redis的使用

目錄 1.當前進度 2.守護進程 3.進程監控 4.玩家姓名添加文件 5.文件刪除玩家姓名 6.redis安裝 7.redis存取命令 8.redis鏈表存取 9.redis程序結構 10.hiredisAPI使用 11.基于redis查找玩家姓名 12.MAKEFILE編寫 13.游戲業務實現總結 1.當前進度 2.守護進程 3.進程監…

db中查詢關于null的sql該怎么寫

正確示例 # 等于null select * from 表名 where 字段名 is NULL; # 不等于null select * from 表名 where 字段名 is not NULL;若需要同時判斷字段不等于某個值且不為null select * from users where age ! 30 and age is not null; select * from users where age ! 30 or a…

從“堆料競賽”到“體驗深耕”,X200 Ultra和X200s打響手機價值升維戰

出品 | 何璽 排版 | 葉媛 vivo雙旗艦來襲&#xff01; 4月21日&#xff0c;vivo X系列春季新品發布會盛大開啟&#xff0c;帶來了一場科技與創新的盛宴。會上&#xff0c;消費者期待已久的X200 Ultra及X200s兩款旗艦新品正式發布。 vivo兩款旗艦新品發布后&#xff0c;其打破…

多模態大語言模型arxiv論文略讀(三十二)

Proximity QA: Unleashing the Power of Multi-Modal Large Language Models for Spatial Proximity Analysis ?? 論文標題&#xff1a;Proximity QA: Unleashing the Power of Multi-Modal Large Language Models for Spatial Proximity Analysis ?? 論文作者&#xff1a…

基于貝葉斯優化的Transformer多輸入單輸出回歸預測模型Bayes-Transformer【MATLAB】

Bayes-Transformer 在機器學習和深度學習領域&#xff0c;Transformer模型已經廣泛應用于自然語言處理、圖像識別、時間序列預測等多個領域。然而&#xff0c;在一些實際應用中&#xff0c;我們面臨著如何高效地優化模型超參數的問題。貝葉斯優化&#xff08;Bayesian Optimiz…

Ruby 正則表達式

Ruby 正則表達式 引言 正則表達式&#xff08;Regular Expression&#xff0c;簡稱Regex&#xff09;是一種強大的文本處理工具&#xff0c;在編程和數據處理中有著廣泛的應用。Ruby 作為一種動態、靈活的編程語言&#xff0c;同樣內置了強大的正則表達式功能。本文將詳細介紹…

kubernetes》》k8s》》刪除命名空間

使用 kubectl delete ns 命名空間 --force --grace-period0 如果還刪除不掉 需要 kubectl get namespace 命名空間 -o json > x.json vim x.json kubectl replace --raw “/api/v1/namespaces/命名空間/finalize” -f ./x.json

玩轉Docker | 使用Docker部署DashMachine個人書簽工具

玩轉Docker | 使用Docker部署DashMachine個人書簽工具 前言一、DashMachine介紹DashMachine簡介DashMachine使用場景二、系統要求環境要求環境檢查Docker版本檢查檢查操作系統版本三、部署DashMachine服務下載鏡像創建容器創建容器檢查容器狀態檢查服務端口安全設置四、訪問Das…

SQL進階知識:一、高級查詢

今天介紹下關于高級查詢的詳細介紹&#xff0c;包括子查詢、連接查詢、分組查詢等&#xff0c;并結合MySQL數據庫提供實際例子。 一、子查詢&#xff08;Subqueries&#xff09; 子查詢是嵌套在另一個查詢中的查詢語句&#xff0c;通常用于提供條件過濾、生成臨時數據集等。子…

【Git】Git Revert 命令詳解

Git Revert 命令詳解 1. Git Revert 的基本概念 Git Revert 是一個用于撤銷特定提交的命令。與 Git Reset 不同&#xff0c;Git Revert 不會更改提交歷史&#xff0c;而是會創建一個新的提交來撤銷指定提交的更改。這意味著&#xff0c;使用 Git Revert 后&#xff0c;項目的…

華為S系列交換機CPU占用率高問題排查與解決方案

問題概述 在華為S系列交換機(V100&V200版本)運行過程中&#xff0c;CPU占用率過高是一個常見問題&#xff0c;可能導致設備性能下降甚至業務中斷。根據華為官方維護寶典&#xff0c;導致CPU占用率高的主要原因可分為四大類&#xff1a;網絡攻擊、網絡震蕩、網絡環路和硬件…