溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

golang垃圾回收原理是什么

發(fā)布時間:2022-01-12 14:12:14 來源:億速云 閱讀:121 作者:柒染 欄目:云計算

golang垃圾回收原理是什么,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

并發(fā)的垃圾回收

golang 語言設(shè)計的根本性追求就是高并發(fā),低延遲,所以golang 的垃圾回收也是持續(xù)在優(yōu)化。golang 的垃圾回收是并發(fā)垃圾回收設(shè)計,業(yè)務(wù)運行和回收器運行并發(fā),這種設(shè)計的初衷是降低垃圾回收停頓時間。

之前提過一個例子,如果你要安全的實現(xiàn)回收垃圾,那么簡單的就是回收垃圾的時候,把所有的業(yè)務(wù)操作都停止,這個術(shù)語是STW(stop the world)。下面用一些術(shù)語:

  1. 賦值器:這個就是程序的業(yè)務(wù)代碼
  2. 回收器:垃圾回收器
 

STW 安全的回收

下面畫了一個圖,表示了這種簡單例子的一個演進:

  1. 灰色表示一次垃圾回收操作
  2. 黑色表示下一次
golang垃圾回收原理是什么
 

 

圖里我們看到:

  1. 在單處理器的場景,賦值器和回收器的代碼是交替運行的,回收器回收的時間即為賦值器停頓的時間;
  2. 多處理器的時候,多個賦值器線程并行執(zhí)行,但是每次回收器回收的時候,還是要掛起多個賦值器;
  3. 第三種,就是讓多個處理器并行的執(zhí)行回收器的任務(wù),減少停頓;

這是一個顯而易見的實現(xiàn)和優(yōu)化演進,其實再進一步,我們可以把完整的一個回收任務(wù)拆分成小粒度的,搞成一次次增量的回收,這樣單次的停頓時間就更少了。

 

并發(fā)的垃圾回收

golang 明顯不是這個(哈哈,曾經(jīng)是),golang 必須要讓賦值器和回收器并發(fā)起來,不能有明顯的停頓。golang 當(dāng)前的垃圾回收特點:

  1. 完全消除了明顯的 STW (除了開啟的垃圾回收的時候)
  2. 回收器標(biāo)記、回收過程完全和賦值器并發(fā)
    1. 但是注意一點,對于單個棧來說,是一個一個掛起掃描的,這種掃描方式叫做 on-the-fly collection,并且是增量式的;

golang 的回收沒有混合屏障之前,一直是插入寫屏障,由于棧賦值沒有 hook 的原因,所以有 STW,混合寫屏障之后,就沒有 STW。

這里有個點要理解:STW 是全局的賦值器掛起,我們一直說 golang 消除了 STW 說的是沒有了全局性的掛起,但是局部的賦值器掛起是一直有的,包括現(xiàn)在也是有的。

golang垃圾回收原理是什么
 
 

插入寫屏障

 

偽代碼

Write 操作改變特定內(nèi)存的值。改操作引發(fā)內(nèi)存存儲,需要三個參數(shù):指向源的指針、待修改域的索引、待存儲的值。寫賦值操作用偽代碼表示下:

Write(src, i, val):    src[i] <- val
 

我們的插入寫屏障就是在這段賦值代碼中,添加一段 hook 代碼,這段 hook 代碼就是所謂的屏障代碼,由編譯器在編譯期生成。寫屏障的實現(xiàn)有多種,golang 使用的是 Dijkstra 算法實現(xiàn):

atomic Write(src, i, ref)    src[i] <- ref    if isBlack(src)        shade(ref)
 

這段偽代碼我們非常容易看懂,就是加了后面的一個判讀邏輯,如果 src 已經(jīng)是黑色的,那么就把指向的新對象置灰色。

 

對象丟失的必要條件

之前的文章有提到三色標(biāo)記法,提到,如果要想出現(xiàn)對象丟失(錯誤的回收)那么必須是同時滿足兩個條件:

  • 條件1:賦值器把白色對象的引用寫入給黑色對象了(換句話說,黑色對象指向白色對象了)
  • 條件2:從灰色對象出發(fā),最終到達該白色對象的所有路徑都被賦值器破壞(換句話說,這個已經(jīng)被黑色指向的白色對象,還沒有在灰色對象的保護下)

圖示舉例:

golang垃圾回收原理是什么
 

 
  1. 賦值器操作一:X -> Z
  2. 賦值器操作二:Y -> null
  3. 回收器操作一:Scan Y
  4. 回收器操作二:回收 Z (這就有問題了)

在這兩個條件同時出現(xiàn)的時候,才會出現(xiàn)對象被錯誤的回收。然后我們回過頭看下寫屏障的實現(xiàn),就會發(fā)現(xiàn),寫屏障從根本上破壞了第一個條件的出現(xiàn)。

 

寫屏障是怎么解決問題?

加了屏障的示意圖:

golang垃圾回收原理是什么
 

 

插入寫屏障就是這么簡單。只要你保證時時刻刻沒有黑色對象指向白色對象的條件出現(xiàn),那么回收的正確性就能保證。但是話又說回來了,這個屏障是配合賦值器回收器并發(fā)的場景才需要,如果你允許直接STW執(zhí)行回收器邏輯,那就不需要這么復(fù)雜了,當(dāng)然啦,這樣的話賦值器的性能肯定就不行啦。

雖然插入寫屏障能解決問題,但是 golang 針對棧上對象的賦值卻沒有捕捉(沒有生成寫屏障),原因自然是性能損耗和實現(xiàn)復(fù)雜度的考慮。這就開了一個例外的口子,有一些黑色的棧對象指向了白色的對象,而回收器卻無法感知到。golang 的解決方法是:最后再 STW 重新掃描一把棧。這個自然就會導(dǎo)致整個進程的賦值器卡頓,所以后面 golang 是引用混合寫屏障解決這個問題。

看完上述內(nèi)容,你們掌握golang垃圾回收原理是什么的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI