Golang垃圾回收GC

結論:雖然Go如今的基於三色標識算法的垃圾回收已經被普遍承認,可是對比於JVM這種運行在虛擬機的GC(GO是在Runtime),仍是有明顯的差距。

 這裏分別取了兩篇文檔的深度分析做爲對比:

1. https://www.dushibaiyu.com/2016/12/java-go-gc.html

Go 併發垃圾收集器

因爲 Go 是一種命令式語言,它的值類型,內存訪問模式和 C# (.NET 使用分代垃圾收集器)至關。php

事實上,Go 程序一般是處理 request/response 任務(如 HTTP 服務器),這意味着 Go 程序表現出強烈的代際行爲,Go 團隊正在探索潛在的能夠利用代際假說的算法,他們稱之爲「面向請求的垃圾收集器」。這本質上是一個能夠策略調優的分代垃圾收集器。在處理請求/響應這種模式時,經過確保年輕代足夠大以使經過處理請求產生的全部垃圾都在其中來優化 GC。(高可用架構譯者注:指的是 Go下一代垃圾收集器 Transaction-Oriented Collector)html

儘管如此,Go 的當前 GC 是不分代的。只是在後臺運行標記/掃描。(高可用架構譯者注:併發標記清除算法)
這樣使暫停時間很是短 ,但使其餘因素更糟糕。從咱們的基本理論上面咱們能夠看到:java

  • GC吞吐量:GC時間與堆大小同步增加。簡單來講,你的程序使用的內存越多,內存釋放速度就越慢,你的計算機花費的時間就越多。若是你的程序沒有並行化,你能夠不用考慮這個問題。
  • 整理:由於沒有整理,GC 過程會產生內存碎片。程序也不會受益於在緩存中整齊排列的內容。
  • 程序吞吐量:由於GC必須在每一個週期作不少工做,因此會消耗很多CPU時間。
  • 暫停分佈:與程序併發運行的任何垃圾收集器均可能遇到Java中「併發模式失敗」的問題:您的程序建立垃圾的速度比GC線程能夠清除它快。在這種狀況下,runtime別無選擇只能徹底中止程序,等待GC完成垃圾收集。所以當Go團隊聲明GC暫停很是低時,該聲明只能適用於GC具備足夠的CPU時間和空間以完成垃圾回收的狀況。另外,因爲Go編譯器缺少確保線程能夠被快速可靠暫停這一功能,會致使暫停時間是否很低取決於您運行的是什麼類型的代碼(例如,base64 解碼單個 goroutine 中的大 blob 會致使暫停時間上升)。
  • 堆開銷:由於經過標記/掃描收集堆很是慢,您須要大量的空間以確保不會碰見「併發模式故障」。 Go默認使用100%的堆開銷會讓程序須要的內存量增長一倍。

咱們能夠看到這些權衡:python

服務1分配內存多於服務2,所以STW暫停在服務1中較高。但STW暫停持續時間在兩個服務上都降低了一個數量級。咱們看到切換後,兩個服務後在GC中花費的CPU使用率增長了約20%。ios

在這個特定的狀況下,Go 以更慢的收集器爲代價換取暫停時間的數量級降低。這是一個好的權衡嗎?暫停時間已經足夠低嗎?c++

付出更多的硬件成本以得到較低的暫停時間,在一些狀況下未必有意義。若是你的服務器暫停時間從 10msec 下降到 1msec,你的用戶真的會注意到嗎?若是你必須加倍你的機器數量才能達成這一目的呢?程序員

Go 將暫停時間優化做爲首要目標,以致於它彷佛願意將程序減慢至任何數量級,以得到較短暫停。算法

 

2.https://www.icode9.com/content-1-213901.html

 

一. 什麼是垃圾回收

曾幾什麼時候,內存管理是程序員開發應用的一大難題。傳統的系統級編程語言(主要指C/C++)中,程序員必須對內存當心的進行管理操做,控制內存的申請及釋放。稍有不慎,就可能產生內存泄露問題,這種問題不易發現而且難以定位,一直成爲困擾開發者的噩夢。如何解決這個頭疼的問題呢?編程

過去通常採用兩種辦法:緩存

  1. 內存泄露檢測工具。這種工具的原理通常是靜態代碼掃描,經過掃描程序檢測可能出現內存泄露的代碼段。然而檢測工具不免有疏漏和不足,只能起到輔助做用。(Valgrind)
  2. 智能指針。這是c++中引入的自動內存管理方法,經過擁有自動內存管理功能的指針對象來引用對象,是程序員不用太關注內存的釋放,而達到內存自動釋放的目的。這種方法是採用最普遍的作法,可是對程序員有必定的學習成本(並不是語言層面的原生支持),並且一旦有忘記使用的場景依然沒法避免內存泄露。

爲了解決這個問題,後來開發出來的幾乎全部新語言(java,python,php等等)都引入了語言層面的自動內存管理 – 也就是語言的使用者只用關注內存的申請而沒必要關心內存的釋放,內存釋放由虛擬機(virtual machine)或運行時(runtime)來自動進行管理。而這種對再也不使用的內存資源進行自動回收的行爲就被稱爲垃圾回收。

二. 常見的垃圾回收算法

① 引用計數

這是最簡單的一種垃圾回收算法,和以前提到的智能指針殊途同歸。對每一個對象維護一個引用計數,當引用該對象的對象被銷燬或更新時被引用對象的引用計數自動減一,當被引用對象被建立或被賦值給其餘對象時引用計數自動加一。當引用計數爲0時則當即回收對象。

這種方法的優勢是實現簡單,而且內存的回收很及時。這種算法在內存比較緊張和實時性比較高的系統中使用的比較普遍,如ios cocoa框架,php,python等。簡單引用計數算法也有明顯的缺點:

  1. 頻繁更新引用計數下降了性能。一種簡單的解決方法就是編譯器將相鄰的引用計數更新操做合併到一次更新;還有一種方法是針對頻繁發生的臨時變量引用不進行計數,而是在引用達到0時經過掃描堆棧確認是否還有臨時對象引用而決定是否釋放。等等還有不少其餘方法,具體能夠參考這裏。
  2. 循環引用問題。當對象間發生循環引用時引用鏈中的對象都沒法獲得釋放。最明顯的解決辦法是避免產生循環引用,如cocoa引入了strong指針和weak指針兩種指針類型。或者系統檢測循環引用並主動打破循環鏈。固然這也增長了垃圾回收的複雜度。

② 標記清除

該方法分爲兩步,標記從根變量開始迭代得遍歷全部被引用的對象,對可以經過應用遍歷訪問到的對象都進行標記爲「被引用」;標記完成後進行清除操做,對沒有標記過的內存進行回收(回收同時可能伴有碎片整理操做)。

這種方法解決了引用計數的不足,可是也有比較明顯的問題:每次啓動垃圾回收都會暫停當前全部的正常代碼執行,回收是系統響應能力大大下降!固然後續也出現了不少mark&sweep算法的變種(如三色標記法)優化了這個問題。

③ 分代收集

通過大量實際觀察得知,在面向對象編程語言中,絕大多數對象的生命週期都很是短。分代收集的基本思想是,將堆劃分爲兩個或多個稱爲 代(generation)的空間。新建立的對象存放在稱爲 新生代(young generation)中(通常來講,新生代的大小會比 老年代小不少),隨着垃圾回收的重複執行,生命週期較長的對象會被 提高(promotion)到老年代中。所以,新生代垃圾回收和老年代垃圾回收兩種不一樣的垃圾回收方式應運而生,分別用於對各自空間中的對象執行垃圾回收。新生代垃圾回收的速度很是快,比老年代快幾個數量級,即便新生代垃圾回收的頻率更高,執行效率也仍然比老年代垃圾回收強,這是由於大多數對象的生命週期都很短,根本無需提高到老年代。

三. Go的GC工做機制

Go的GC自打一開始就被不少人詬病,通過這麼多年的發展Go的GC已經變得很是的優秀了,如下是Go的GC算法里程碑

  1. v1.1 STW (stop the world)
  2. v1.3 Mark STW, Sweep 並行
  3. v1.5 三色標記法
  4. v1.8 hybrid write barrier

Go語言GC算法主要是基於Mark and Sweep (標記清除)算法,在此基礎上進行改進和優化。

① Mark and Sweep

Mark and Sweep(標記清除)算法主要是如下2個步驟

  1. 標記(Mark): 找出全部不可達對象,而後作上標記
  2. 清除(Sweep): 回收標記好的對象

咱們經過如下圖解來解釋 標記清除 算法是如何工做的

  1. 開始標記,程序暫停,此時程序和對象的關係以下圖所示
  2. 找到全部可達對象,並作上標記
  3. 標記完成後開始清除未標記的對象
  4. 清除完成後,對象以下圖所示

標記清除算法存在如下幾個問題

  1. STW (stop the world) 標記對象的時候程序須要暫停,致使程序出現卡頓 (最主要的問題)
  2. 標記須要掃描整個堆
  3. 清除對象會產生堆碎片

STW指的是runtime把全部的協程都凍結了,意味着用戶邏輯是暫停的,這樣全部的對象都不會被修改,這個時候去掃描是絕對安全的。

② Tri-color Marking

爲了解決標記清除算法帶來的問題,Go在標記清除算法基礎上提出來Tri-color Marking(三色標記法)算法來優化GC過程,大致流程以下

  1. 最開始全部的對象都是白色
  2. GC開始,掃描全部可達的對象,標記爲灰色
  3. 從灰色對象中找到其引用對象並標記爲灰色,本身標記爲黑色
  4. 監控對象修改,循環步驟3,直到沒有任何灰色對象
  5. GC回收白色對象
  6. 最後把全部黑色對象變成白色

三色標記法是怎麼優化STW問題的呢,主要是如下2點

  1. 標記操做和用戶邏輯並行: 用戶邏輯常常會生成或改變對象引用,那如何保證標記和用戶邏輯並行呢?Go爲了解決這個問題引入了寫屏障機制,在GC的過程當中會監控對象的內存修改,並對對象進行從新標記,這個時候用戶邏輯也能夠執行 (其實是很短暫的STW,而後對對象從新標記),因此標記操做能夠作到必定程度和用戶邏輯並行。
  2. 清除操做和用戶邏輯並行: 咱們知道三色標記法中最後只剩下的黑白兩種對象,黑色對象是程序恢復後接着使用的對象,若是不碰觸黑色對象,只清除白色的對象,確定不會影響程序邏輯,因此清除白色對象和用戶邏輯能夠並行。

經過容許用戶邏輯在標記和清除操做上作到並行處理來縮短STW的時間,提高總體GC的性能。

轉自 https://www.icode9.com/content-1-213901.html

相關文章
相關標籤/搜索