Go 部落格
Go GC:優先考慮低延遲和簡潔性
背景
Go 正在構建一個不僅面向 2015 年,更是面向 2025 年及以後的垃圾回收器 (GC):一個能夠支援當今 軟體開發,並隨未來十年新軟體和硬體一起擴充套件的 GC。這樣的未來不容許 全程式暫停(stop-the-world)的 GC 停頓,這曾是 Go 等安全語言更廣泛應用的阻礙。
Go 1.5 作為這一未來的初次呈現,其 GC 延遲遠低於我們一年前設定的 10 毫秒目標。我們在 Gophercon 的一次演講中展示了一些令人印象深刻的數字。延遲的改進引起了廣泛關注;Robin Verlangen 的部落格文章 數十億請求與 Go 1.5 相遇 透過端到端結果驗證了我們的方向。我們 還特別喜歡 Alan Shreve 的生產伺服器圖表以及他“天吶,減少了 85%”的 評論。
如今,16 GB 記憶體只需 100 美元,CPU 具有多核心,每個核心有多個硬體執行緒。十年後,這些硬體看起來會很過時,但如今用 Go 構建的軟體將需要擴充套件以滿足不斷增長的需求和下一個重大事物。考慮到硬體將提供提高吞吐量的能力,Go 的垃圾回收器被設計成傾向於低延遲,並且只通過一個旋鈕進行調優。Go 1.5 是邁向這條道路的第一大步,這些初步步驟將永遠影響 Go 及其最能支援的應用。這篇部落格文章對我們在 Go 1.5 收集器方面所做的工作進行了高層次的概述。
細節闡述
為了建立一個面向未來十年的垃圾回收器,我們轉向了幾十年前的一種演算法。Go 的新垃圾回收器是一個併發、三色、標記-清除 收集器,這一思想最早由 Dijkstra 於 1978 年提出。這與當今大多數“企業級” 垃圾回收器有所不同,我們認為它非常適合現代硬體的特性和現代軟體的延遲要求。
在三色收集器中,每個物件要麼是白色、灰色或黑色,我們將堆視為一個連線物件的圖。在 GC 週期開始時,所有物件都是白色。GC 訪問所有根物件,這些物件是應用程式直接可訪問的,例如全域性變數和棧上的物件,並將它們標記為灰色。然後,GC 選擇一個灰色物件,將其標記為黑色,然後掃描它以查詢指向其他物件的指標。當掃描發現一個指向白色物件的指標時,它將該物件變為灰色。這個過程重複進行,直到沒有更多灰色物件。此時,白色物件被認為是不可達的,可以被重用。
這一切都與應用程式併發發生,應用程式被稱為修改器(mutator),它在收集器執行時改變指標。因此,修改器必須維護一個不變式:沒有黑色物件指向白色物件,以免垃圾回收器丟失對已訪問堆部分中的物件的跟蹤。維護這個不變式是寫屏障(write barrier)的工作,這是一個由修改器在堆中的指標被修改時執行的小函式。Go 的寫屏障將當前是白色的、現在可達的物件標記為灰色,確保垃圾回收器最終會掃描它以查詢指標。
決定何時完成查詢所有灰色物件的工作是很微妙的,並且如果我們想避免阻塞修改器,它可能會昂貴且複雜。為了保持簡單,Go 1.5 儘可能多地併發工作,然後短暫地全程式暫停(stop-the-world)以 檢查所有潛在的灰色物件源。找到這次最終全程式暫停所需的時間與本次 GC 執行的總工作量之間的最佳平衡點是 Go 1.6 的一項主要交付成果。
當然,魔鬼藏在細節中。我們何時開始一個 GC 週期?我們用什麼指標來做出這個決定?GC 應該如何與 Go 排程器互動?我們如何暫停一個修改器執行緒足夠長的時間來掃描其棧? 我們如何表示白色、灰色和黑色,以便高效地找到並掃描灰色物件?我們如何知道根物件在哪裡?我們如何知道物件中的指標位於何處?我們如何最大程度地減少記憶體 碎片?我們如何處理快取效能問題?堆應該有多大?等等等等,有些與記憶體分配有關,有些與查詢可達物件有關,有些與排程有關,但許多都與效能有關。對這些領域中每一個方面的低層討論都超出了這篇部落格 文章的範圍。
在更高層面,解決效能問題的一種方法是新增 GC 旋鈕,每個效能問題對應一個旋鈕。然後程式設計師可以轉動這些旋鈕,為他們的應用程式尋找合適的設定。缺點 是,經過十年,每年新增一兩個 旋鈕後,您最終會面臨“GC 旋鈕調整員 就業法案”。Go 不會走這條路。相反,我們提供一個單一的旋鈕,稱為 GOGC。這個值 控制著堆的總大小相對於可達物件大小的比例。預設值 100 意味著上次回收後,總堆大小現在比可達物件大小大 100%(即兩倍)。200 意味著總堆大小比可達物件大小大 200%(即三倍)。如果您想減少花費在 GC 上的總時間,請 增加 GOGC。如果您想用更多的 GC 時間換取更少的記憶體,請 降低 GOGC。
更重要的是,隨著下一代硬體的出現,記憶體翻倍,簡單地將 GOGC 加倍將使 GC 週期數減少一半。另一方面,由於 GOGC 基於可達物件的大小,將可達物件翻倍來使負載翻倍無需重新調整。應用程式只需擴充套件即可。此外, 由於不受持續支援數十個旋鈕的束縛, 執行時團隊可以根據真實客戶應用程式的反饋來專注於改進執行時。
結論
Go 1.5 的 GC 開啟了一個未來,其中全程式暫停不再是轉向安全語言的障礙。 在這個未來中,應用程式將隨著硬體輕鬆擴充套件,並且隨著硬體變得更加強大,GC 將不再是更好、 更具可伸縮性的軟體的障礙。對於未來十年乃至更遠,這是一個很好的位置。有關 1.5 GC 以及我們如何消除延遲問題的更多詳細資訊,請參閱 Go GC:延遲問題已解決(演示)或 幻燈片。
下一篇文章: Golang UK 2015
上一篇文章: Go 1.5 釋出了
部落格索引