go gc垃圾回收——三色標(biāo)記、混合寫屏障

一、什么是GC

垃圾回收(Garbage Collection,簡稱GC)是編程語言中提供的自動(dòng)的內(nèi)存管理機(jī)制,自動(dòng)釋放不需要的對象,讓出存儲器資源,無需程序員手動(dòng)執(zhí)行。

Golang中的垃圾回收主要應(yīng)用三色標(biāo)記法,GC過程和其他用戶goroutine可并發(fā)運(yùn)行,但需要一定時(shí)間的STW(stop the world),STW的過程中,CPU不執(zhí)行用戶代碼,全部用于垃圾回收,這個(gè)過程的影響很大,Golang進(jìn)行了多次的迭代優(yōu)化來解決這個(gè)問題。

1.1 Golang GC 發(fā)展史:

  • go1.1,提高效率和垃圾回收精確度。

標(biāo)記-清除(mark and sweep)算法

  • go1.3,提高了垃圾回收的精確度。
  • go1.4,之前版本的runtime大部分是使用C寫的,這個(gè)版本大量使用Go進(jìn)行了重寫,讓GC有了掃描stack的能力,進(jìn)一步提高了垃圾回收的精確度。
  • go1.5,目標(biāo)是降低GC延遲,采用了并發(fā)標(biāo)記并發(fā)清除三色標(biāo)記write barrier,以及實(shí)現(xiàn)了更好的回收器調(diào)度,設(shè)計(jì)文檔1文檔2,以及2015 版的Go talk

三色標(biāo)記——“強(qiáng)-弱” 三色不變式、插入屏障、刪除屏障

  • go1.6,小優(yōu)化,當(dāng)程序使用大量內(nèi)存時(shí),GC暫停時(shí)間有所降低。
  • go1.7,小優(yōu)化,當(dāng)程序有大量空閑goroutine,stack大小波動(dòng)比較大時(shí),GC暫停時(shí)間有顯著降低。
  • go1.8write barrier切換到hybrid write barrier,以消除STW中的re-scan,把STW的最差情況降低到50us設(shè)計(jì)文檔

三色標(biāo)記——混合屏障

  • go1.9,提升指標(biāo)比較多,(1)過去 runtime.GC, debug.SetGCPercent, 和 debug.FreeOSMemory都不能觸發(fā)并發(fā)GC,他們觸發(fā)的GC都是阻塞的,go1.9可以了,變成了在垃圾回收之前只阻塞調(diào)用GC的goroutine。(2)debug.SetGCPercent只在有必要的情況下才會觸發(fā)GC。
  • go.1.10,小優(yōu)化,加速了GC,程序應(yīng)當(dāng)運(yùn)行更快一點(diǎn)點(diǎn)。
  • go1.12,顯著提高了堆內(nèi)存存在大碎片情況下的sweeping性能,能夠降低GC后立即分配內(nèi)存的延遲。
  • go1.13,著手解決向操作系統(tǒng)歸還內(nèi)存的,提出了新的 Scavenger
  • go1.14,替代了僅存活了一個(gè)版本的 Scavenger,全新的頁分配器,優(yōu)化分配內(nèi)存過程的速率與現(xiàn)有的擴(kuò)展性問題,并引入了異步搶占,解決了由于密集循環(huán)導(dǎo)致的 STW 時(shí)間過長的問題
  • ......

二、常見的GC算法

  1. 引用計(jì)數(shù)法

根據(jù)對象自身的引用計(jì)數(shù)來回收,當(dāng)引用計(jì)數(shù)歸零時(shí)進(jìn)行回收,但是計(jì)數(shù)頻繁更新會帶來更多開銷,且無法解決循環(huán)引用的問題。

  • 優(yōu)點(diǎn):簡單直接,回收速度快
  • 缺點(diǎn):需要額外的空間存放計(jì)數(shù),無法處理循環(huán)引用的情況;
  1. 標(biāo)記清除法

標(biāo)記出所有不需要回收的對象,在標(biāo)記完成后統(tǒng)一回收掉所有未被標(biāo)記的對象。


  • 優(yōu)點(diǎn):簡單直接,速度快,適合可回收對象不多的場景
  • 缺點(diǎn):會造成不連續(xù)的內(nèi)存空間(內(nèi)存碎片),導(dǎo)致有大的對象創(chuàng)建的時(shí)候,明明內(nèi)存中總內(nèi)存是夠的,但是空間不是連續(xù)的造成對象無法分配;
  1. 復(fù)制法

復(fù)制法將內(nèi)存分為大小相同的兩塊,每次使用其中的一塊,當(dāng)這一塊的內(nèi)存使用完后,將還存活的對象復(fù)制到另一塊去,然后再把使用的空間一次清理掉。


  • 優(yōu)點(diǎn):解決了內(nèi)存碎片的問題,每次清除針對的都是整塊內(nèi)存,但是因?yàn)橐苿?dòng)對象需要耗費(fèi)時(shí)間,效率低于標(biāo)記清除法;
  • 缺點(diǎn):有部分內(nèi)存總是利用不到,資源浪費(fèi),移動(dòng)存活對象比較耗時(shí),并且如果存活對象較多的時(shí)候,需要擔(dān)保機(jī)制確保復(fù)制區(qū)有足夠的空間可完成復(fù)制;
  1. 標(biāo)記整理

標(biāo)記過程同標(biāo)記清除法,結(jié)束后將存活對象壓縮至一端,然后清除邊界外的內(nèi)容。


  • 優(yōu)點(diǎn):解決了內(nèi)存碎片的問題,也不像標(biāo)記復(fù)制法那樣需要擔(dān)保機(jī)制,存活對象較多的場景也使適用;
  • 缺點(diǎn):性能低,因?yàn)樵谝苿?dòng)對象的時(shí)候不僅需要移動(dòng)對象還要維護(hù)對象的引用地址,可能需要對內(nèi)存經(jīng)過幾次掃描才能完成;
  1. 分代式

將對象根據(jù)存活時(shí)間的長短進(jìn)行分類,存活時(shí)間小于某個(gè)值的為年輕代,存活時(shí)間大于某個(gè)值的為老年代,永遠(yuǎn)不會參與回收的對象為永久代。并根據(jù)分代假設(shè)(如果一個(gè)對象存活時(shí)間不長則傾向于被回收,如果一個(gè)對象已經(jīng)存活很長時(shí)間則傾向于存活更長時(shí)間)對對象進(jìn)行回收。

三、go 1.3之前的標(biāo)記-清除(mark and sweep)算法

此算法主要有兩個(gè)主要的步驟:

  • 標(biāo)記(Mark phase)
  • 清除(Sweep phase)

第一步,暫停程序業(yè)務(wù)邏輯, 找出不可達(dá)的對象,然后做上標(biāo)記。第二步,回收標(biāo)記好的對象。

操作非常簡單,但是有一點(diǎn)需要額外注意:mark and sweep算法在執(zhí)行的時(shí)候,需要程序暫停!即 STW(stop the world)。也就是說,這段時(shí)間程序會卡在哪兒。

第二步, 開始標(biāo)記,程序找出它所有可達(dá)的對象,并做上標(biāo)記。如下圖所示:

第三步, 標(biāo)記完了之后,然后開始清除未標(biāo)記的對象. 結(jié)果如下.


第四步, 停止暫停,讓程序繼續(xù)跑。然后循環(huán)重復(fù)這個(gè)過程,直到process程序生命周期結(jié)束。

3.1 標(biāo)記-清掃(mark and sweep)的缺點(diǎn)

  • STW,stop the world;讓程序暫停,程序出現(xiàn)卡頓 (重要問題)
  • 標(biāo)記需要掃描整個(gè)heap
  • 清除數(shù)據(jù)會產(chǎn)生heap碎片

所以Go V1.3版本之前就是以上來實(shí)施的, 流程是


Go V1.3 做了簡單的優(yōu)化,將STW提前, 減少STW暫停的時(shí)間范圍.如下所示

這里面最重要的問題就是:mark-and-sweep 算法會暫停整個(gè)程序

Go是如何面對并這個(gè)問題的呢?接下來G V1.5版本 就用三色并發(fā)標(biāo)記法來優(yōu)化這個(gè)問題.

四、go 1.5的三色并發(fā)標(biāo)記法

三色標(biāo)記法 實(shí)際上就是通過三個(gè)階段的標(biāo)記來確定清楚的對象都有哪些. 我們來看一下具體的過程.

第一步 , 就是只要是新創(chuàng)建的對象,默認(rèn)的顏色都是標(biāo)記為“白色”.

這里面需要注意的是, 所謂“程序”, 則是一些對象的跟節(jié)點(diǎn)集合.

所以上圖,可以轉(zhuǎn)換如下的方式來表示.

第二步, 每次GC回收開始, 然后從根節(jié)點(diǎn)開始遍歷所有對象,把遍歷到的對象從白色集合放入“灰色”集合。

第三步, 遍歷灰色集合,將灰色對象引用的對象從白色集合放入灰色集合,之后將此灰色對象放入黑色集合

第四步, 重復(fù)第三步, 直到灰色中無任何對象.


image

第五步: 回收所有的白色標(biāo)記表的對象. 也就是回收垃圾.

以上便是三色并發(fā)標(biāo)記法, 不難看出,我們上面已經(jīng)清楚的體現(xiàn)三色的特性, 那么又是如何實(shí)現(xiàn)并行的呢?

Go是如何解決標(biāo)記-清除(mark and sweep)算法中的卡頓(stw,stop the world)問題的呢?

4.1 沒有STW的三色標(biāo)記法

我們還是基于上述的三色并發(fā)標(biāo)記法來說, 他是一定要依賴STW的. 因?yàn)槿绻粫和3绦? 程序的邏輯改變對象引用關(guān)系, 這種動(dòng)作如果在標(biāo)記階段做了修改,會影響標(biāo)記結(jié)果的正確性。我們舉一個(gè)場景.

如果三色標(biāo)記法, 標(biāo)記過程不使用STW將會發(fā)生什么事情?

可以看出,有兩個(gè)問題, 在三色標(biāo)記法中,是不希望被發(fā)生的

  • 條件1: 一個(gè)白色對象被黑色對象引用(白色被掛在黑色下)
  • 條件2: 灰色對象與它之間的可達(dá)關(guān)系的白色對象遭到破壞(灰色同時(shí)丟了該白色)

當(dāng)以上兩個(gè)條件同時(shí)滿足時(shí), 就會出現(xiàn)對象丟失現(xiàn)象!

當(dāng)然, 如果上述中的白色對象3, 如果他還有很多下游對象的話, 也會一并都清理掉.

為了防止這種現(xiàn)象的發(fā)生,最簡單的方式就是STW,直接禁止掉其他用戶程序?qū)ο笠藐P(guān)系的干擾,但是STW的過程有明顯的資源浪費(fèi),對所有的用戶程序都有很大影響,如何能在保證對象不丟失的情況下合理的盡可能的提高GC效率,減少STW時(shí)間呢?

答案就是, 那么我們只要使用一個(gè)機(jī)制,來破壞上面的兩個(gè)條件就可以了.

4.2 屏障機(jī)制

我們讓GC回收器,滿足下面兩種情況之一時(shí),可保對象不丟失. 所以引出兩種方式.

4.2.1 “強(qiáng)-弱” 三色不變式
  • 強(qiáng)三色不變式

不存在黑色對象引用到白色對象的指針。

  • 弱三色不變式

所有被黑色對象引用的白色對象都處于灰色保護(hù)狀態(tài).

為了遵循上述的兩個(gè)方式,Golang團(tuán)隊(duì)初步得到了如下具體的兩種屏障方式“插入屏障”, “刪除屏障”.

4.2.2 插入屏障

具體操作: 在A對象引用B對象的時(shí)候,B對象被標(biāo)記為灰色。(將B掛在A下游,B必須被標(biāo)記為灰色)

滿足: 強(qiáng)三色不變式. (不存在黑色對象引用白色對象的情況了, 因?yàn)榘咨珪?qiáng)制變成灰色)

偽碼如下:

添加下游對象(當(dāng)前下游對象slot, 新下游對象ptr) {   
  //1
  標(biāo)記灰色(新下游對象ptr)   

  //2
  當(dāng)前下游對象slot = 新下游對象ptr                    
}

場景:

A.添加下游對象(nil, B)   //A 之前沒有下游, 新添加一個(gè)下游對象B, B被標(biāo)記為灰色
A.添加下游對象(C, B)     //A 將下游對象C 更換為B,  B被標(biāo)記為灰色

這段偽碼邏輯就是寫屏障,. 我們知道,黑色對象的內(nèi)存槽有兩種位置, . 棧空間的特點(diǎn)是容量小,但是要求相應(yīng)速度快,因?yàn)楹瘮?shù)調(diào)用彈出頻繁使用, 所以“插入屏障”機(jī)制,在棧空間的對象操作中不使用. 而僅僅使用在堆空間對象的操作中.

接下來,我們用幾張圖,來模擬整個(gè)一個(gè)詳細(xì)的過程, 希望您能夠更可觀的看清晰整體流程。

但是如果棧不添加,當(dāng)全部三色標(biāo)記掃描之后,棧上有可能依然存在白色對象被引用的情況(如上圖的對象9). 所以要對棧重新進(jìn)行三色標(biāo)記掃描, 但這次為了對象不丟失, 要對本次標(biāo)記掃描啟動(dòng)STW暫停. 直到棧空間的三色標(biāo)記結(jié)束.

最后將棧和堆空間 掃描剩余的全部 白色節(jié)點(diǎn)清除. 這次STW大約的時(shí)間在10~100ms間.

4.2.3 刪除屏障

具體操作: 被刪除的對象,如果自身為灰色或者白色,那么被標(biāo)記為灰色。

滿足: 弱三色不變式. (保護(hù)灰色對象到白色對象的路徑不會斷)

偽代碼:

添加下游對象(當(dāng)前下游對象slot, 新下游對象ptr) {
  //1
  if (當(dāng)前下游對象slot是灰色 || 當(dāng)前下游對象slot是白色) {
          標(biāo)記灰色(當(dāng)前下游對象slot)     //slot為被刪除對象, 標(biāo)記為灰色
  }

  //2
  當(dāng)前下游對象slot = 新下游對象ptr
}

場景:

A.添加下游對象(B, nil)   //A對象,刪除B對象的引用。  B被A刪除,被標(biāo)記為灰(如果B之前為白)
A.添加下游對象(B, C)         //A對象,更換下游B變成C。   B被A刪除,被標(biāo)記為灰(如果B之前為白)

接下來,我們用幾張圖,來模擬整個(gè)一個(gè)詳細(xì)的過程, 希望您能夠更可觀的看清晰整體流程。

這種方式的回收精度低,一個(gè)對象即使被刪除了最后一個(gè)指向它的指針也依舊可以活過這一輪,在下一輪GC中被清理掉。

五、go 1.8的混合寫屏障(hybrid write barrier)機(jī)制

插入寫屏障和刪除寫屏障的短板:

  • 插入寫屏障:結(jié)束時(shí)需要STW來重新掃描棧,標(biāo)記棧上引用的白色對象的存活;
  • 刪除寫屏障:回收精度低,GC開始時(shí)STW掃描堆棧來記錄初始快照,這個(gè)過程會保護(hù)開始時(shí)刻的所有存活對象。

Go V1.8版本引入了混合寫屏障機(jī)制(hybrid write barrier),避免了對棧re-scan的過程,極大的減少了STW的時(shí)間。結(jié)合了兩者的優(yōu)點(diǎn)。

5.1 混合寫屏障規(guī)則

具體操作:

1、GC開始將棧上的對象全部掃描并標(biāo)記為黑色(之后不再進(jìn)行第二次重復(fù)掃描,無需STW),

2、GC期間,任何在棧上創(chuàng)建的新對象,均為黑色。

3、被刪除的對象標(biāo)記為灰色。

4、被添加的對象標(biāo)記為灰色。

滿足: 變形的弱三色不變式.

偽代碼:

添加下游對象(當(dāng)前下游對象slot, 新下游對象ptr) {
      //1 
        標(biāo)記灰色(當(dāng)前下游對象slot)    //只要當(dāng)前下游對象被移走,就標(biāo)記灰色

      //2 
      標(biāo)記灰色(新下游對象ptr)

      //3
      當(dāng)前下游對象slot = 新下游對象ptr
}

這里我們注意, 屏障技術(shù)是不在棧上應(yīng)用的,因?yàn)橐WC棧的運(yùn)行效率。

5.2 混合寫屏障的具體場景分析

接下來,我們用幾張圖,來模擬整個(gè)一個(gè)詳細(xì)的過程, 希望您能夠更可觀的看清晰整體流程。

注意混合寫屏障是Gc的一種屏障機(jī)制,所以只是當(dāng)程序執(zhí)行GC的時(shí)候,才會觸發(fā)這種機(jī)制。

GC開始:掃描棧區(qū),將可達(dá)對象全部標(biāo)記為黑

5.2.1 場景一: 對象被一個(gè)堆對象刪除引用,成為棧對象的下游

偽代碼

//前提:堆對象4->對象7 = 對象7;  //對象7 被 對象4引用
棧對象1->對象7 = 堆對象7;  //將堆對象7 掛在 棧對象1 下游
堆對象4->對象7 = null;    //對象4 刪除引用 對象7

5.2.2 場景二: 對象被一個(gè)棧對象刪除引用,成為另一個(gè)棧對象的下游

偽代碼

new 棧對象9;
對象8->對象3 = 對象3;      //將棧對象3 掛在 棧對象9 下游
對象2->對象3 = null;      //對象2 刪除引用 對象3

5.2.3 場景三:對象被一個(gè)堆對象刪除引用,成為另一個(gè)堆對象的下游

偽代碼

堆對象10->對象7 = 堆對象7;       //將堆對象7 掛在 堆對象10 下游
堆對象4->對象7 = null;         //對象4 刪除引用 對象7

5.2.4 場景四:對象從一個(gè)棧對象刪除引用,成為另一個(gè)堆對象的下游

偽代碼

堆對象10->對象7 = 堆對象7;       //將堆對象7 掛在 堆對象10 下游
堆對象4->對象7 = null;         //對象4 刪除引用 對象7

Golang中的混合寫屏障滿足弱三色不變式,結(jié)合了刪除寫屏障和插入寫屏障的優(yōu)點(diǎn),只需要在開始時(shí)并發(fā)掃描各個(gè)goroutine的棧,使其變黑并一直保持,這個(gè)過程不需要STW,而標(biāo)記結(jié)束后,因?yàn)闂T趻呙韬笫冀K是黑色的,也無需再進(jìn)行re-scan操作了,減少了STW的時(shí)間。

六、垃圾回收過程

6.1 Marking setup

為了打開寫屏障,必須停止每個(gè)goroutine,讓垃圾收集器觀察并等待每個(gè)goroutine進(jìn)行函數(shù)調(diào)用, 等待函數(shù)調(diào)用是為了保證goroutine停止時(shí)處于安全點(diǎn)。


// 如果goroutine4 處于如下循環(huán)中,運(yùn)行時(shí)間取決于slice numbers的大小
func add(numbers []int) int {
    var v int
    for _, n := range numbers {
             v += n
     }
     return v
}

下面的代碼中,由于for{} 循環(huán)所在的goroutine 永遠(yuǎn)不會中斷,導(dǎo)致始終無法進(jìn)入STW階段,資源浪費(fèi);Go 1.14 之后,此類goroutine 能被異步搶占,使得進(jìn)入STW的時(shí)間不會超過搶占信號觸發(fā)的周期,程序也不會因?yàn)閮H僅等待一個(gè)goroutine的停止而停頓在進(jìn)入STW之前的操作上。

func main() {
    go func() {
        for {
        }
    }()
    time.Sleep(time.Milliecond)
    runtime.GC()
    println("done")
}

6.2 Marking

一旦寫屏障打開,垃圾收集器就開始標(biāo)記階段,垃圾收集器所做的第一件事是占用25%CPU。

標(biāo)記階段需要標(biāo)記在堆內(nèi)存中仍然在使用中的值。首先檢查所有現(xiàn)goroutine的堆棧,以找到堆內(nèi)存的根指針。然后收集器必須從那些根指針遍歷堆內(nèi)存圖,標(biāo)記可以回收的內(nèi)存。

當(dāng)存在新的內(nèi)存分配時(shí),會暫停分配內(nèi)存過快的那些 goroutine,并將其轉(zhuǎn)去執(zhí)行一些輔助標(biāo)記(Mark Assist)的工作,從而達(dá)到放緩繼續(xù)分配、輔助 GC 的標(biāo)記工作的目的。


6.3 Mark終止

關(guān)閉寫屏障,執(zhí)行各種清理任務(wù)(STW - optional )


6.4 Sweep (清理)

清理階段用于回收標(biāo)記階段中標(biāo)記出來的可回收內(nèi)存。當(dāng)應(yīng)用程序goroutine嘗試在堆內(nèi)存中分配新內(nèi)存時(shí),會觸發(fā)該操作,清理導(dǎo)致的延遲和吞吐量降低被分散到每次內(nèi)存分配時(shí)。


清除階段出現(xiàn)新對象:

清除階段是掃描整個(gè)堆內(nèi)存,可以知道當(dāng)前清除到什么位置,創(chuàng)建的新對象判定下,如果新對象的指針位置已經(jīng)被掃描過了,那么就不用作任何操作,不會被誤清除,如果在當(dāng)前掃描的位置的后面,把該對象的顏色標(biāo)記為黑色,這樣就不會被誤清除了

什么時(shí)候進(jìn)行清理?

主動(dòng)觸發(fā)(runtime.GC()) 被動(dòng)觸發(fā) (GC百分比、定時(shí))

七、關(guān)注指標(biāo)與調(diào)優(yōu)示例

7.1 關(guān)注指標(biāo)

Go 的 GC 被設(shè)計(jì)為成比例觸發(fā)、大部分工作與賦值器并發(fā)、不分代、無內(nèi)存移動(dòng)且會主動(dòng)向操作系統(tǒng)歸還申請的內(nèi)存。因此最主要關(guān)注的、能夠影響賦值器的性能指標(biāo)有:

  • CPU 利用率:回收算法會在多大程度上拖慢程序?有時(shí)候,這個(gè)是通過回收占用的 CPU 時(shí)間與其它 CPU 時(shí)間的百分比來描述的。
  • GC 停頓時(shí)間:回收器會造成多長時(shí)間的停頓?目前的 GC 中需要考慮 STW 和 Mark Assist 兩個(gè)部分可能造成的停頓。
  • GC 停頓頻率:回收器造成的停頓頻率是怎樣的?目前的 GC 中需要考慮 STW 和 Mark Assist 兩個(gè)部分可能造成的停頓。
  • GC 可擴(kuò)展性:當(dāng)堆內(nèi)存變大時(shí),垃圾回收器的性能如何?但大部分的程序可能并不一定關(guān)心這個(gè)問題。

7.2 調(diào)優(yōu)示例

7.2.1 合理化內(nèi)存分配的速度、提高賦值器的 CPU 利用率

goroutine 的執(zhí)行時(shí)間占其生命周期總時(shí)間非常短的一部分,但大部分時(shí)間都花費(fèi)在調(diào)度器的等待上了,說明同時(shí)創(chuàng)建大量 goroutine 對調(diào)度器產(chǎn)生的壓力確實(shí)不小,我們不妨將這一產(chǎn)生速率減慢,一批一批地創(chuàng)建 goroutine。

func concat() {
   for n := 0; n < 800; n++ {
      go func() {
            s := "Go GC"
            s += " " + "Hello"
            s += " " + "World"
            _ = s
         }()
   }
}

//改進(jìn)
func concat() {
   wg := sync.WaitGroup{}
   for n := 0; n < 100; n++ {
      wg.Add(8)
      for i := 0; i < 8; i++ {
         go func() {
            s := "Go GC"
            s += " " + "Hello"
            s += " " + "World"
            _ = s
            wg.Done()
         }()
      }
      wg.Wait()
   }
}
7.2.2 降低并復(fù)用已經(jīng)申請的內(nèi)存

newBuf()產(chǎn)生的申請的內(nèi)存過多, sync.Pool 是內(nèi)存復(fù)用的一個(gè)最為顯著的例子

func newBuf() []byte {
   return make([]byte, 10<<20)
}
b := newBuf()

//改進(jìn)
var bufPool = sync.Pool{
    New: func() interface{} {
        return make([]byte, 10<<20)
    },
}
b := bufPool.Get().([]byte)
7.2.3 調(diào)整 GOGC

降低收集器的啟動(dòng)頻率(提高GC百分比)無法幫助垃圾收集器更快完成收集工作。降低頻率會導(dǎo)致垃圾收集器在收集期間完成更多的工作。 可以通過減少新分配對象數(shù)量來幫助垃圾收集器更快完成收集工作。

7.3 小結(jié)

控制內(nèi)存分配的速度,限制 goroutine 的數(shù)量,從而提高賦值器對 CPU 的利用率。
減少并復(fù)用內(nèi)存,例如使用 sync.Pool 來復(fù)用需要頻繁創(chuàng)建臨時(shí)對象,例如提前分配足夠的內(nèi)存來降低多余的拷貝。
需要時(shí),增大 GOGC 的值,降低 GC 的運(yùn)行頻率。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,978評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內(nèi)容