go - 內存分配機制詳解


一般程序的內存分配,從高位到低位依次為 

全局靜態區:用於存儲全局變量、靜態變量等;這部分內存在程序編譯時已經分配好,由操作系統管理,速度快,不易出錯。

棧:函數中的基礎類型的局部變量;由程序進行系統調用向操作系統申請,由操作系統管理,速度快。每個線程有自己的棧區。

堆:使用malloc或new申請的內存;由程序運行過程中動態分配任意大小的內存,由程序管理,使用free或者delete刪除;頻繁的分配和釋放必然導致內存碎片。

常量區:存放常量字符串,程序結束后由系統釋放。

程序代碼區:存放程序的二進制代碼。

 

Go的內存分配:

Go是內置運行時runtime的語言;像這種內置運行時的語言會拋棄傳統的內存管理方式,改為自己管理;這樣可以完成類似預分配,內存池等操作,以避開系統調用產生的性能問題。Go的內存分配可以分為以下幾點:

1. 每次從操作系統申請一大塊內存,由Go來對這塊內存做分配,減少系統調用。

2. 內存分配算法采用 TCMalloc算法,核心思想是把內存分的非常細,進行分級管理,以降低鎖的粒度。

3. 回收對象內存時,並沒有將其真正釋放掉,而是放回預先分配的大內存中,以便復用。只有內存閑置過多的時候,才會嘗試歸還部分內存給操作系統,降低整體開銷。

 

Go在程序啟動的時候,會分配一塊連續的內存(虛擬內存),整體如下:

arena就是所謂的堆區,他把內存分割成 8K大小的頁,頁是heap中的最小管理單位,一些頁組合起來形成了mspan。

bitmap區用於保存arena對應地址(指針大小為 8B,bitmap中一個byte大小的內存對應arena區域中的4個指針,因此大小為 512G/(4 * 8B))中是否保存了對象,以及對象是否被gc過,主要用於gc。

spans區域存放了mspan的指針,用於表示arena區的某一頁屬於哪個mspan。大小為 512G / 8KB(頁的大小) * 8B(指針大小)。在創建 mspan的時候,按頁填充對應的spans區域,在回收object時,很容易找到他所屬的mspan。

 

Go內存管理結構

1. class和mspan

go對象管理粒度分為67種大小,也叫class和塊。一個page根據class大小分為8K/class size。

class相同且地址連續的pages組成一個span。mspan是span的實際結構,mspan之間組成雙向鏈表關聯。

 

2. mcache 

每個工作線程都有各自的mcache,本地緩存可用的mspan資源,這樣各個線程在申請內存的時候無需加鎖,對於小於32KB大小的對象,直接通過mcache分配;對於大於23KB的大對象,則需要在mheap中分配。

每個goroutine對於同一個class都有scan和noscan兩種mspan隊列,其中scan分配給含有指針的對象,noscan分配給不含指針的對象,這樣做是為了便於進行垃圾回收,在進行垃圾回收的時候,對於不包含指針的對象,無需進一步掃描是否引用其他活躍對象。

3. mcenter

mcenter為所有mcache提供切分好的mspan。有多少種mspan類型的mspan就有多少個mcenter,每個mcenter保存一種特定類型的mspan,提供兩個mspan雙端隊列,包括已分配出去的和未分配出去的。

所有線程共享,需要加鎖訪問,但是申請一種 類型的mspan不會影響其他的mcenter.

type mcentral struct {
        lock      mutex
        spanclass spanClass
        nonempty  mSpanList // list of spans with a free object, ie a nonempty free list
        empty     mSpanList // list of spans with no free objects (or cached in an mcache)

        // nmalloc is the cumulative count of objects allocated from
        // this mcentral, assuming all spans in mcaches are
        // fully-allocated. Written atomically, read under STW.
        nmalloc uint64
}

4. mheap

代表Go程序的堆空間,Go程序使用一個mheap來管理堆空間。

當mcenter中沒有空閑的mspan時,會向mheap申請。而mheap沒有剩余內存時,會向操作系統申請新內存。

另外,對於大於32Kb的大對象,需要在mheap中分配,所有線程共享,需要加鎖。

 

參考:

https://zhuanlan.zhihu.com/p/59125443

https://www.cnblogs.com/unqiang/p/12052308.html

https://zhuanlan.zhihu.com/p/128462868

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM