天天看點

go 記憶體配置設定

1、程式每次先從系統申請一大塊記憶體(比如1MB),減少向系統申請記憶體頻率,也就是說,先給我整塊大的,以後少找你,不夠了,再找你要一塊大的;

2、然後程式将大塊記憶體,按照特定大小(後文将提到的sizeClass,機關可以了解為8位元組)切分為小塊(object),小塊構成連結清單(span);

3、為對象配置設定記憶體時,隻需要按照對象的大小(sizeClass),從滿足該大小的連結清單中,擷取一小塊即可;

4、回收對象記憶體時,将該小塊記憶體重新歸還到原連結清單,一遍複用;

5、如果閑置的很多,則會嘗試歸還部分記憶體給系統,降低程式整體開銷

注意上述回收,是指記憶體配置設定器的回收,記憶體配置設定器隻管理記憶體塊,并不關心對象狀态,且不會主動回收記憶體。隻有在垃圾回收器完成回收操作後,觸發記憶體配置設定器回收記憶體

span: 由位址連續的頁(page)組成

object:将span按照特定大小的塊切分成多個小塊(object),每個小塊用于對象存儲。按8位元組倍數分為N種

是不是有點懵,其實可以這麼了解,就像上學時寫作業

程式向系統申請的一大塊記憶體(比如1MB),這一大塊記憶體,可以看成我們新買了的作業本;

接着,制定一些書簽,書簽上需要标明sizeClass,當後續給不同大小的對象配置設定記憶體時,能夠快速定位;

作業本一些連續的頁(page)就組成了span,然後對每頁上的一行一行進行分塊,可以了解為分object,但是需要根據書簽(sizeClass)大小分塊,比如書簽為1的,我們就像書簽下的這些頁上的每一行分成一塊object

Go起點高,直接采用tcMalloc(線程緩存記憶體)成熟架構

配置設定器由三種元件組成(元件去管理span,擷取和釋放object塊):

cache: 每個運作工作線程都會綁定一個cache,用于無鎖object配置設定

central:為所有cache提供切分好的後備span資源

heap:管理閑置span,需要時向系統申請或釋放記憶體

go 記憶體配置設定

配置設定器按照頁數區分不同大小的span,比如 mheap ,以頁數為機關将span 存放在管理數組中,需要時就以頁數為索引進行查找。當然span大小并非

固定不變,在擷取閑置的span時,如果沒有找到合适大小的span,那就傳回頁數更多的span,此時引發裁剪操作,多餘部分将構成新的span 被放回管理數組。配置設定器

還會嘗試将相鄰的空閑 span 合并,以構成更大的記憶體塊,減少碎片,實作更靈活的配置設定政策。

用于存儲的object,按照8位元組倍數分為N種。比如說,大小為24位元組的object可以用來存儲範圍為17——24位元組的對象。雖然會有一些浪費,但是配置設定器隻需要面對有限幾種規格(sizeclass)的小塊記憶體,優化了配置設定和複用政策。

同時,配置設定器會嘗試将多個微小的對象組合到一個object記憶體塊,以節約記憶體

配置設定器初始化時,會建構對照表,存儲大小和規格的對應關系,包括用來切分的span頁數(一頁8KB)。

若對象大小超出特定的門檻值(32KB),會被當做大對象特殊處理。

1、計算待配置設定對象對應規格(sizeClass),也就是要幾個8位元組;

2、從cache.alloc 數組中找到對應規格的span;

3、從span.freelsit 連結清單中擷取可用的object;

4、若沒有可用的,即span.freelsit為空,從central擷取span

5、如central.noempty為空,從heap.free 或者 heap.freelarge 中擷取span,并切分object 連結清單;

6、若還是沒有找到大小合适且空閑的span,則向作業系統申請新記憶體塊

1、将标記為可回收的object交給所屬span.freelist

2、該span被放回central,也就是拼接至mcentral.nonempty連結清單後,但是不要以為mcache.alloc 數組中就沒有該span,

該span還在,任然保持對span的指針引用;

3、如果span收回了所有的object,則将其還給heap,即mheap.freelist,以便重新分割複用;

4、定期掃描heap長時間閑置的span,釋放其占用的記憶體,也就是還給系統

注意,以上不包含大對象,他直接從heap配置設定和回收

作為工作線程私有且不被共享的cache是實作高性能無鎖配置設定的關鍵,而central的作用是在多個cache間提高object使用率,避免記憶體浪費,将span歸還heap,是為了在不同規格object需求間平衡。

在計算機科學裡,沒有什麼問題是不能通過中間過程解決的,是以很多架構,都會有中間件這個存在。

假如cache擷取span的一部分object後,那麼該span中還有許多剩餘的object,但是回收操作将該span交還給central,該span還可以給其他線程cache1,cache2...使用,cache并沒有持有span,隻是用span中object。

而歸還到heap的過程,則可以這部分記憶體,被其他不同規格大小需求使用,重新切分。