Go 部落格
Go 執行時:四年後
自 2018 年 關於 Go GC 的上一篇博文以來,Go GC 以及更廣泛的 Go 執行時一直在穩步改進。我們承擔了一些大型專案,這些專案源於真實的 Go 程式和 Go 使用者面臨的實際挑戰。讓我們來回顧一下亮點!
有什麼新功能?
-
sync.Pool
,一個 GC 感知的記憶體重用工具,其延遲影響更低,並且比以前更有效地回收記憶體。(Go 1.13) -
Go 執行時將不需要的記憶體更積極地歸還給作業系統,從而減少了過多的記憶體消耗和記憶體不足錯誤的發生機率。這使得閒置記憶體消耗減少了多達 20%。(Go 1.13 和 1.14)
-
在許多情況下,Go 執行時能夠更及時地搶佔 goroutine,將“停止世界”的延遲最多降低 90%。在此處觀看 Gophercon 2020 的演講。(Go 1.14)
-
Go 執行時比以前更有效地管理計時器,尤其是在擁有大量 CPU 核心的機器上。(Go 1.14)
-
使用
defer
語句延遲的函式呼叫在大多數情況下,其開銷幾乎與常規函式呼叫一樣低。在此處觀看 Gophercon 2020 的演講。(Go 1.14) -
記憶體分配器的慢路徑與 CPU 核心的結合度更好,擴充套件性更好,在高度並行的程式中,吞吐量最多提高 10%,尾部延遲最多降低 30%。(Go 1.14 和 1.15)
-
現在可以透過更細粒度、更靈活、更高效的 API,即 runtime/metrics 包來訪問 Go 記憶體統計資訊。這使得獲取執行時統計資訊的延遲降低了兩個數量級(從毫秒降至微秒)。(Go 1.16)
-
Go 排程器花費在尋找新工作上的 CPU 時間最多減少 30%。(Go 1.17)
-
在 amd64、arm64 和 ppc64 上,Go 程式碼現在遵循基於暫存器的呼叫約定,CPU 效率最多提高 15%。(Go 1.17 和 Go 1.18)
-
Go GC 的內部計數和排程進行了重新設計,解決了與效率和健壯性相關的各種長期存在的問題。這使得在 goroutine 堆疊佔記憶體很大一部分的應用程式中,應用程式尾部延遲顯著降低(最多 66%)。(Go 1.18)
-
Go GC 現在限制了在應用程式空閒時自身的 CPU 使用量。這使得在非常空閒的應用程式中,GC 週期期間的 CPU 利用率降低了 75%,從而減少了可能導致作業排程器混淆的 CPU 峰值。(Go 1.19)
這些更改對使用者來說大部分是不可見的:他們喜歡的 Go 程式碼透過升級 Go 版本就能執行得更好。
一個新配置項
Go 1.19 帶來了一項長期請求的功能,該功能需要一些額外的操作才能使用,但具有巨大的潛力:Go 執行時的軟記憶體限制。
多年來,Go GC 只有一個調整引數:GOGC
。GOGC
允許使用者調整Go GC 造成的 CPU 開銷和記憶體開銷之間的權衡。多年來,這個“配置項”很好地服務了 Go 社群,涵蓋了各種各樣的用例。
Go 執行時團隊一直不願向 Go 執行時新增新的配置項,這並非沒有原因:每一個新的配置項都代表著配置空間中一個新的維度,我們需要對其進行測試和維護,並且可能需要永遠如此。此外,配置項的泛濫也給 Go 開發人員帶來了理解和有效使用它們的負擔,而配置項越多,這種負擔就越重。因此,Go 執行時一直傾向於在最少的配置下也能表現良好。
那麼,為什麼要新增記憶體限制配置項呢?
記憶體不像 CPU 時間那樣可以隨意替換。CPU 時間,如果你稍等片刻,未來總會有更多的。但記憶體是有限的。
記憶體限制解決了兩個問題。
首先,當應用程式的峰值記憶體使用不可預測時,僅靠 GOGC
幾乎無法防止記憶體耗盡。僅使用 GOGC
,Go 執行時根本不知道它有多少可用的記憶體。設定記憶體限制可以使執行時能夠應對短暫的、可恢復的負載峰值,因為它知道何時需要更努力地工作以減少記憶體開銷。
其次,為了在不使用記憶體限制的情況下避免記憶體不足錯誤,必須根據峰值記憶體來調整 GOGC
,從而導致更高的 GC CPU 開銷以維持較低的記憶體開銷,即使在應用程式未達到峰值記憶體使用且有大量記憶體可用時也是如此。這在我們容器化的世界中尤其重要,程式被放置在具有特定和隔離記憶體限制的盒子中;我們應該充分利用它們!透過提供應對負載峰值的保護,設定記憶體限制可以使 GOGC
在 CPU 開銷方面進行更積極的調整。
記憶體限制的設計易於採用且健壯。例如,它限制的是應用程式 Go 部分的整體記憶體佔用,而不僅僅是 Go 堆,因此使用者不必擔心計算 Go 執行時的開銷。執行時還會根據記憶體限制調整其記憶體回收策略,從而更積極地響應記憶體壓力,將記憶體歸還給作業系統。
但是,雖然記憶體限制是一個強大的工具,但仍需謹慎使用。一個主要的警告是,它可能會使你的程式面臨 GC 抖動:即程式花費過多時間執行 GC,導致無法進行有意義的進展。例如,如果記憶體限制設定得太低,不足以滿足程式實際需要的記憶體量,Go 程式可能會出現抖動。這在以前不太可能發生,除非 GOGC
被明確地大大傾向於記憶體使用。我們選擇優先耗盡記憶體而不是出現抖動,因此作為一種緩解措施,執行時會將 GC 限制在總 CPU 時間的 50%,即使這意味著超過記憶體限制。
所有這些都需要仔細考慮,因此作為這項工作的一部分,我們釋出了一個全新的 GC 指南,其中包含互動式視覺化,幫助您理解 GC 的成本以及如何對其進行調整。
結論
試試記憶體限制!在生產環境中使用它!閱讀GC 指南!
我們一直在尋求關於如何改進 Go 的反饋,同時也很樂意聽到它對您來說就是好用的情況。給我們傳送反饋!
下一篇文章:Go 的十三年
上一篇文章:2022 年第二季度 Go 開發者調查結果
部落格索引