用 GODEBUG 看 GC

image

原文地址:用 GODEBUG 看 GChtml

什麼是 GC

在計算機科學中,垃圾回收(GC)是一種自動管理內存的機制,垃圾回收器會去嘗試回收程序再也不使用的對象及其佔用的內存。而最先 John McCarthy 在 1959 年左右發明了垃圾回收,以簡化 Lisp 中的手動內存管理的機制(來自 wikipedia)。git

爲何要 GC

手動管理內存挺麻煩,管錯或者管漏內存也很糟糕,將會直接致使程序不穩定(持續泄露)甚至直接崩潰。github

GC 帶來的問題

硬要說會帶來什麼問題的話,也就數你們最關注的 Stop The World(STW),STW 代指在執行某個垃圾回收算法的某個階段時,須要將整個應用程序暫停去處理 GC 相關的工做事項。例如:golang

行爲 會不會 STW 爲何
標記開始 在開始標記時,準備根對象的掃描,會打開寫屏障(Write Barrier) 和 輔助GC(mutator assist),而回收器和應用程序是併發運行的,所以會暫停當前正在運行的全部 Goroutine。
併發標記中 不會 標記階段,主要目的是標記堆內存中仍在使用的值。
標記結束 在完成標記任務後,將從新掃描部分根對象,這時候會禁用寫屏障(Write Barrier)和輔助GC(mutator assist),而標記階段和應用程序是併發運行的,因此在標記階段可能會有新的對象產生,所以在從新掃描時須要進行 STW。

如何調整 GC 頻率

能夠經過 GOGC 變量設置初始垃圾收集器的目標百分比值,對比的規則爲當新分配的數值與上一次收集後剩餘的實時數值的比例達到設置的目標百分比時,就會觸發 GC,默認值爲 GOGC=100。若是將其設置爲 GOGC=off 能夠徹底禁用垃圾回收器,要不試試?算法

簡單來說就是,GOGC 的值設置的越大,GC 的頻率越低,但每次最終所觸發到 GC 的堆內存也會更大。c#

各版本 GC 狀況

版本 GC 算法 STW 時間
Go 1.0 STW(強依賴 tcmalloc) 百ms到秒級別
Go 1.3 Mark STW, Sweep 並行 百ms級別
Go 1.5 三色標記法, 併發標記清除。同時運行時從 C 和少許彙編,改成 Go 和少許彙編實現 10-50ms級別
Go 1.6 1.5 中一些與併發 GC 不協調的地方更改,集中式的 GC 協調協程,改成狀態機實現 5ms級別
Go 1.7 GC 時由 mark 棧收縮改成併發,span 對象分配機制由 freelist 改成 bitmap 模式,SSA引入 ms級別
Go 1.8 混合寫屏障(hybrid write barrier), 消除 re-scanning stack sub ms
Go 1.12 Mark Termination 流程優化 sub ms, 但幾乎減小一半

注:資料來源於 @boya 在深圳 Gopher Meetup 的分享。併發

GODEBUG

GODEBUG 變量能夠控制運行時內的調試變量,參數以逗號分隔,格式爲:name=val。本文着重點在 GC 的觀察上,主要涉及 gctrace 參數,咱們經過設置 gctrace=1 後就能夠使得垃圾收集器向標準錯誤流發出 GC 運行信息。post

涉及術語

  • mark:標記階段。
  • markTermination:標記結束階段。
  • mutator assist:輔助 GC,是指在 GC 過程當中 mutator 線程會併發運行,而 mutator assist 機制會協助 GC 作一部分的工做。
  • heap_live:在 Go 的內存管理中,span 是內存頁的基本單元,每頁大小爲 8kb,同時 Go 會根據對象的大小不一樣而分配不一樣頁數的 span,而 heap_live 就表明着全部 span 的總大小。
  • dedicated / fractional / idle:在標記階段會分爲三種不一樣的 mark worker 模式,分別是 dedicated、fractional 和 idle,它們表明着不一樣的專一程度,其中 dedicated 模式最專一,是完整的 GC 回收行爲,fractional 只會幹部分的 GC 行爲,idle 最輕鬆。這裏你只須要了解它是不一樣專一程度的 mark worker 就行了,詳細介紹咱們能夠等後續的文章。

演示代碼

func main() {
    wg := sync.WaitGroup{}
    wg.Add(10)
    for i := 0; i < 10; i++ {
        go func(wg *sync.WaitGroup) {
            var counter int
            for i := 0; i < 1e10; i++ {
                counter++
            }
            wg.Done()
        }(&wg)
    }

    wg.Wait()
}

gctrace

$ GODEBUG=gctrace=1 go run main.go    
gc 1 @0.032s 0%: 0.019+0.45+0.003 ms clock, 0.076+0.22/0.40/0.80+0.012 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 2 @0.046s 0%: 0.004+0.40+0.008 ms clock, 0.017+0.32/0.25/0.81+0.034 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 3 @0.063s 0%: 0.004+0.40+0.008 ms clock, 0.018+0.056/0.32/0.64+0.033 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 4 @0.080s 0%: 0.004+0.45+0.016 ms clock, 0.018+0.15/0.34/0.77+0.065 ms cpu, 4->4->1 MB, 5 MB goal, 4 P
gc 5 @0.095s 0%: 0.015+0.87+0.005 ms clock, 0.061+0.27/0.74/1.8+0.023 ms cpu, 4->4->1 MB, 5 MB goal, 4 P
gc 6 @0.113s 0%: 0.014+0.69+0.002 ms clock, 0.056+0.23/0.48/1.4+0.011 ms cpu, 4->4->1 MB, 5 MB goal, 4 P
gc 7 @0.140s 1%: 0.031+2.0+0.042 ms clock, 0.12+0.43/1.8/0.049+0.17 ms cpu, 4->4->1 MB, 5 MB goal, 4 P
...

格式

gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # P

含義

  • gc#:GC 執行次數的編號,每次疊加。
  • @#s:自程序啓動後到當前的具體秒數。
  • #%:自程序啓動以來在GC中花費的時間百分比。
  • #+...+#:GC 的標記工做共使用的 CPU 時間佔總 CPU 時間的百分比。
  • #->#-># MB:分別表示 GC 啓動時, GC 結束時, GC 活動時的堆大小.
  • #MB goal:下一次觸發 GC 的內存佔用閾值。
  • #P:當前使用的處理器 P 的數量。

案例

gc 7 @0.140s 1%: 0.031+2.0+0.042 ms clock, 0.12+0.43/1.8/0.049+0.17 ms cpu, 4->4->1 MB, 5 MB goal, 4 P
  • gc 7:第 7 次 GC。
  • @0.140s:當前是程序啓動後的 0.140s。
  • 1%:程序啓動後到如今共花費 1% 的時間在 GC 上。
  • 0.031+2.0+0.042 ms clock:優化

    • 0.031:表示單個 P 在 mark 階段的 STW 時間。
    • 2.0:表示全部 P 的 mark concurrent(併發標記)所使用的時間。
    • 0.042:表示單個 P 的 markTermination 階段的 STW 時間。
  • 0.12+0.43/1.8/0.049+0.17 ms cpu:spa

    • 0.12:表示整個進程在 mark 階段 STW 停頓的時間。
    • 0.43/1.8/0.049:0.43 表示 mutator assist 佔用的時間,1.8 表示 dedicated + fractional 佔用的時間,0.049 表示 idle 佔用的時間。
    • 0.17ms:0.17 表示整個進程在 markTermination 階段 STW 時間。
  • 4->4->1 MB:

    • 4:表示開始 mark 階段前的 heap_live 大小。
    • 4:表示開始 markTermination 階段前的 heap_live 大小。
    • 1:表示被標記對象的大小。
  • 5 MB goal:表示下一次觸發 GC 回收的閾值是 5 MB。
  • 4 P:本次 GC 一共涉及多少個 P。

總結

經過本章節咱們掌握了使用 GODEBUG 查看應用程序 GC 運行狀況的方法,只要用這種方法咱們就能夠觀測不一樣狀況下 GC 的狀況了,甚至能夠作出很是直觀的對比圖,你們不妨嘗試一下。

關聯文章

參考

相關文章
相關標籤/搜索