欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang內(nèi)存管理之內(nèi)存分配器詳解

 更新時間:2023年06月30日 11:36:20   作者:IguoChan  
Go內(nèi)存分配器的設(shè)計思想來源于TCMalloc,全稱是Thread-Caching?Malloc,核心思想是把內(nèi)存分為多級管理,下面就來和大家深入聊聊Go語言內(nèi)存分配器的使用吧

0. 簡介

程序中的數(shù)據(jù)都會被分配到程序所在的虛擬內(nèi)存中,內(nèi)存空間包含兩個重要區(qū)域:棧(Stack)堆(Heap)。函數(shù)調(diào)用的參數(shù)、返回值和局部變量大部分會分配在棧上,這部分由編譯器管理。堆內(nèi)存的管理方式視語言而定:

  • C/C++等編程語言的堆內(nèi)存由工程師主動申請和釋放;
  • Go、Java等編程語言由工程師和編譯器/運行時共同管理,其內(nèi)存由內(nèi)存分配器分配,由垃圾回收器回收。

本文就介紹一下Go語言的內(nèi)存分配器。

1. Go內(nèi)存分配設(shè)計原理

Go內(nèi)存分配器的設(shè)計思想來源于TCMalloc,全稱是Thread-Caching Malloc,核心思想是把內(nèi)存分為多級管理,利用緩存的思想提升內(nèi)存使用效率,降低鎖的粒度。

在堆內(nèi)存管理上分為三個內(nèi)存級別:

  • 線程緩存(MCache):作為線程獨立的內(nèi)存池,與線程的第一交互內(nèi)存,訪問無需加鎖;
  • 中心緩存(MCentral):作為線程緩存的下一級,是多個線程共享的,所以訪問時需要加鎖;
  • 頁堆(MHeap):中心緩存的下一級,在遇到32KB以上的對象時,會直接選擇頁堆分配大內(nèi)存,而當頁堆內(nèi)存不夠時,則會通過系統(tǒng)調(diào)用向系統(tǒng)申請內(nèi)存。

1.1 內(nèi)存管理基本單元mspan

//go:notinheap
type mspan struct {
   next *mspan     // next span in list, or nil if none
   prev *mspan     // previous span in list, or nil if none
   list *mSpanList // For debugging. TODO: Remove.
   startAddr uintptr // address of first byte of span aka s.base()
   npages    uintptr // number of pages in span
   freeindex uintptr
   allocBits  *gcBits
   gcmarkBits *gcBits
   allocCache uint64
   ...
}

runtime.mspan是Go內(nèi)存管理的基本單元,其結(jié)構(gòu)體中包含的nextprev指針,分別指向前后的runtime.mspan,所以其串聯(lián)后的結(jié)構(gòu)是一個雙向鏈表。

startAddr表示此mspan的起始地址,npages表示管理的頁數(shù),每頁大小8KB,這個頁不是操作系統(tǒng)的內(nèi)存頁,一般是操作系統(tǒng)內(nèi)存頁的整數(shù)倍。

其它字段:

  • freeindex — 掃描頁中空閑對象的初始索引;
  • allocBits 和 gcmarkBits — 分別用于標記內(nèi)存的占用和回收情況;
  • allocCache — allocBits 的補碼,可以用于快速查找內(nèi)存中未被使用的內(nèi)存;

注意使用//go:notinheap標記次結(jié)構(gòu)體mspan為非堆上類型,保證此類型對象不會逃逸到堆上。

圖示:

跨度類

mspan中有一個字段是spanclass,稱為跨度類,是對mspan大小級別的劃分,每個mspan能夠存放指定范圍大小的對象,32KB以內(nèi)的小對象在Go中,會對應(yīng)不同大小的內(nèi)存刻度Size Class,Size Class和Object Size是一一對應(yīng)的,前者指序號 0、1、2、3,后者指具體對象大小 0B、8B、16B、24B

//go:notinheap
type mspan struct {
   ...
   spanclass   spanClass     // size class and noscan (uint8)
   ...
}

Go 語言的內(nèi)存管理模塊中一共包含 67 種跨度類,每一個跨度類都會存儲特定大小的對象并且包含特定數(shù)量的頁數(shù)以及對象,所有的數(shù)據(jù)都會被預(yù)選計算好并存儲在runtime.class_to_sizeruntime.class_to_allocnpages等變量中:

classbytes/objbytes/spanobjectstail wastemax waste
1881921024087.50%
2168192512043.75%
3248192341029.24%
4328192256046.88%
54881921703231.52%
6648192128023.44%
78081921023219.07%
6732768327681012.50%

上表展示了對象大小從 8B 到 32KB,總共 67 種跨度類的大小、存儲的對象數(shù)以及浪費的內(nèi)存空間,以表中的第四個跨度類為例,跨度類為 5 的runtime.mspan中對象的大小上限為 48 字節(jié)、管理 1 個頁、最多可以存儲 170 個對象。因為內(nèi)存需要按照頁進行管理,所以在尾部會浪費 32 字節(jié)的內(nèi)存,當頁中存儲的對象都是 33 字節(jié)時,最多會浪費 31.52% 的資源:

((48−33)∗170+32)/8192=0.31518

除了上述 67 個跨度類之外,運行時中還包含 ID 為 0 的特殊跨度類,它能夠管理大于 32KB 的特殊對象。

1.2 線程緩存(mcache)

runtime.mcache是Go語言中的線程緩存,它會與線程上的處理器意義綁定,用于緩存用戶程序申請的微小對象。每一個線程緩存都持有numSpanClasses個(68∗2)個mspan,存儲在mcachealloc字段中:

//go:notinheap
type mcache struct {
   ...
   alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass
   ...
}

1.3 中心緩存(mcentral)

每個中心緩存都會管理某個跨度類的內(nèi)存管理單元,它會同時持有兩個runtime.spanSet,分別存儲包含空閑對象和不包含空閑對象的內(nèi)存管理單元,訪問中心緩存中的內(nèi)存管理單元需要使用互斥鎖。

如圖上所示,是 runtime.mcentral 中的 spanSet 的內(nèi)存結(jié)構(gòu),index 字段是一個uint64類型數(shù)字的地址,該uint64的數(shù)字按32位分為前后兩半部分head和tail,向spanSet中插入和獲取mspan有其提供的push和pop函數(shù),以push函數(shù)為例,會根據(jù)index的head,對spanSetBlock數(shù)據(jù)塊包含的mspan的個數(shù)512取商,得到spanSetBlock數(shù)據(jù)塊所在的地址,然后head對512取余,得到要插入的mspan在該spanSetBlock數(shù)據(jù)塊的具體地址。之所以是512,因為spanSet指向的spanSetBlock數(shù)據(jù)塊是一個包含512個mspan的集合。

由全部spanClass規(guī)格的runtime.mcentral共同組成的緩存結(jié)構(gòu)如下:

1.4 頁堆(mheap)

//go:notinheap
type mheap struct {
   ...
   arenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena
   ...
   central [numSpanClasses]struct {
      mcentral mcentral
      pad      [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte
   }
   ...
}

runtime.mheap是內(nèi)存分配的核心結(jié)構(gòu)體,其最重要的兩個字段如上。

在Go中其被作為全局變量mheap_存儲:

var mheap_ mheap

頁堆中包含一個長度為numSpanClasses個(68∗2)個的runtime.mcentral數(shù)組,其中 68 個為跨度類需要 scan 的中心緩存,另外的 68 個是 noscan (沒有指針,無需掃描)的中心緩存。

arenas是heapArena的二維數(shù)組的集合。如下:

2. 內(nèi)存分配

堆上所有的對象內(nèi)存分配都會通過runtime.newobject進行分配,運行時根據(jù)對象大小將它們分為微對象、小對象和大對象:

  • 微對象(0, 16B):先使用微型分配器,再依次嘗試線程緩存、中心緩存和堆分配內(nèi)存;多個小于16B的無指針微對象的內(nèi)存分配請求,會合并向Tiny微對象空間申請,微對象的 16B 內(nèi)存空間從 spanClass 為 4 或 5(無GC掃描)的mspan中獲取。
  • 小對象[16B, 32KB]:先向mcache申請,mcache內(nèi)存空間不夠時,向mcentral申請,mcentral不夠,則向頁堆mheap申請,再不夠就向操作系統(tǒng)申請。
  • 大對象(32KB, +∞):大對象直接向頁堆mheap申請。

對于內(nèi)存的釋放,遵循逐級釋放的策略。當ThreadCache的緩存充足或者過多時,則會將內(nèi)存退還給CentralCache。當CentralCache內(nèi)存過多或者充足,則將低命中內(nèi)存塊退還PageHeap。

以上就是Golang內(nèi)存管理之內(nèi)存分配器詳解的詳細內(nèi)容,更多關(guān)于Golang內(nèi)存分配器的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 自動生成代碼controller?tool的簡單使用

    自動生成代碼controller?tool的簡單使用

    這篇文章主要為大家介紹了自動生成代碼controller?tool的簡單使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • C語言的10大基礎(chǔ)算法

    C語言的10大基礎(chǔ)算法

    算法是一個程序和軟件的靈魂,作為一名優(yōu)秀的程序員,只有對一些基礎(chǔ)的算法有著全面的掌握,才會在設(shè)計程序和編寫代碼的過程中顯得得心應(yīng)手。這篇文章主要介紹了C語言的10大基礎(chǔ)算法,需要的朋友可以參考下
    2019-09-09
  • golang解析json數(shù)據(jù)的4種方法總結(jié)

    golang解析json數(shù)據(jù)的4種方法總結(jié)

    在日常工作中每一名開發(fā)者,不管是前端還是后端,都經(jīng)常使用 JSON,下面這篇文章主要給大家介紹了關(guān)于golang解析json數(shù)據(jù)的4種方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-06-06
  • go循環(huán)依賴的最佳解決方案

    go循環(huán)依賴的最佳解決方案

    ? import cycle not allowed(循環(huán)依賴不被允許)相信作為每一個golang語言使用研發(fā),都遇到過這個令人頭痛的報錯,循環(huán)依賴是指兩個或多個模塊之間互相依賴,形成了一個閉環(huán)的情況,本文會結(jié)合部分案例對解決方案進行講解,需要的朋友可以參考下
    2023-10-10
  • Go語言中sync.Cond使用詳解

    Go語言中sync.Cond使用詳解

    本文主要介紹了Go語言中sync.Cond使用詳解,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 詳解Golang中創(chuàng)建error的方式總結(jié)與應(yīng)用場景

    詳解Golang中創(chuàng)建error的方式總結(jié)與應(yīng)用場景

    Golang中創(chuàng)建error的方式包括errors.New、fmt.Errorf、自定義實現(xiàn)了error接口的類型等,本文主要為大家介紹了這些方式的具體應(yīng)用場景,需要的可以參考一下
    2023-07-07
  • Go語言錯誤處理異常捕獲+異常拋出

    Go語言錯誤處理異常捕獲+異常拋出

    這篇文章主要介紹了Go語言錯誤處理異常捕獲和異常拋出,Go語言的作者認為java等語言的錯誤處理底層實現(xiàn)較為復(fù)雜,就實現(xiàn)了函數(shù)可以返回錯誤類型以及簡單的異常捕獲,雖然簡單但是也非常精妙,大大的提高了運行效率,下文需要的朋友可以參考一下
    2022-02-02
  • Golang之sync.Pool使用詳解

    Golang之sync.Pool使用詳解

    這篇文章主要介紹了Golang之sync.Pool使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧
    2021-05-05
  • go項目打包部署的完整步驟

    go項目打包部署的完整步驟

    之前斷斷續(xù)續(xù)的接觸到項目部署,一直沒有詳細的了解部署,于是最近就好好的專研一下項目的部署,下面這篇文章主要給大家介紹了關(guān)于go項目打包部署的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • 基于GORM實現(xiàn)CreateOrUpdate方法詳解

    基于GORM實現(xiàn)CreateOrUpdate方法詳解

    這篇文章主要為大家介紹了基于GORM實現(xiàn)CreateOrUpdate方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10

最新評論