亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

golang內存分配與管理

2023-10-07 08:41:09
17
0

概述

 
? golang的內存分配機制源自Google的tcmalloc算法,英文全稱thread caching malloc,從名字可以看出,是在原有基礎上,針對多核多線程的內存管理進行優化而提出來的。該算法的核心思想是內存的多級管理,進而降低鎖的粒度;將內存按需劃成大小不一的塊,減少內存的碎片化。為每個P,也就是go協程調度模型了里面的邏輯處理器維護一個mcache結構體的獨立內存池,只有當該內存池不足時,才會向全局mcentral和mheap結構體管理的內存池申請。為每一個P維持一個私有的本地內存池,從而不用加鎖,加快了內存分配速度。只有在本地P的內存池被消耗完,或者申請的內存太大時,才會訪問全局的內存池,大大減小了多線程下對全局內存池訪問帶來的競爭系統損耗。
 

內存架構

 
? 以64位的系統為例,1.11版本以后go程序的整個堆內存是連續,如下圖 所示。結構雖然簡單,但是在混合使用go和c的時候會導致程序崩潰,例如分配的內存地址發生沖突,導致初始化堆和擴容失敗。
 
? 自1.11版本以后,對堆實現了分塊處理,arena不再是連續的,以64位的Linux系統為例,是一個塊塊64MB大小的塊。golang內存的三級架構如下圖所示。下面將分別介紹各個層。
 

mspan

 
? mspan結構體是go內存管理的基本單元,定義在runtime/mheap.go中,主要結構體成員如下:
 
//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
 
   startAddr uintptr // address of first byte of span aka s.base()
   npages    uintptr // number of pages in span
   
   nelems uintptr // number of object in the span.
   
   allocBits  *gcBits
   
   allocCount  uint16        // number of allocated objects
   spanclass   spanClass     // size class and noscan (uint8)
   elemsize    uintptr       // computed from sizeclass or from npages
   .....
}
 
? 可以發現,這是一個雙向鏈表。
 
? startAddr:當前span在arena中的起始字節的地址
 
? npages:當前span包含arena中多少頁
 
? nelems:當前span,包含多少個對象。golang又對每一個span,按照所屬class的不同,切分成大小不同的塊,以減少內存碎片。
 
? allocCount:已分配的對象數目
 
? elemsize:對象大小
 
? spanclass:span所屬的class。
 
? 根據對象的大小,golang劃分了一系列的class,以應對各種場景的內存分配,較少內存碎片化。每個class都有一個固定大小的對象和固定的span大小,如下所示:
 
// class  bytes/obj  bytes/span  objects  waste bytes
//     1          8        8192     1024            0
//     2         16        8192      512            0
//     3         32        8192      256            0
//     4         48        8192      170           32
//     5         64        8192      128            0
//     6         80        8192      102           32
//     7         96        8192       85           32
//     8        112        8192       73           16
//     9        128        8192       64            0
//    10        144        8192       56          128
//    11        160        8192       51           32
//    12        176        8192       46           96
//    13        192        8192       42          128
//    14        208        8192       39           80
//    15        224        8192       36          128
//    16        240        8192       34           32
//    17        256        8192       32            0
//    18        288        8192       28          128
//    19        320        8192       25          192
//    20        352        8192       23           96
//    21        384        8192       21          128
//    22        416        8192       19          288
//    23        448        8192       18          128
//    24        480        8192       17           32
//    25        512        8192       16            0
//    26        576        8192       14          128
//    27        640        8192       12          512
//    28        704        8192       11          448
//    29        768        8192       10          512
//    30        896        8192        9          128
//    31       1024        8192        8            0
//    32       1152        8192        7          128
//    33       1280        8192        6          512
//    34       1408       16384       11          896
//    35       1536        8192        5          512
//    36       1792       16384        9          256
//    37       2048        8192        4            0
//    38       2304       16384        7          256
//    39       2688        8192        3          128
//    40       3072       24576        8            0
//    41       3200       16384        5          384
//    42       3456       24576        7          384
//    43       4096        8192        2            0
//    44       4864       24576        5          256
//    45       5376       16384        3          256
//    46       6144       24576        4            0
//    47       6528       32768        5          128
//    48       6784       40960        6          256
//    49       6912       49152        7          768
//    50       8192        8192        1            0
//    51       9472       57344        6          512
//    52       9728       49152        5          512
//    53      10240       40960        4            0
//    54      10880       32768        3          128
//    55      12288       24576        2            0
//    56      13568       40960        3          256
//    57      14336       57344        4            0
//    58      16384       16384        1            0
//    59      18432       73728        4            0
//    60      19072       57344        3          128
//    61      20480       40960        2            0
//    62      21760       65536        3          256
//    63      24576       24576        1            0
//    64      27264       81920        3          128
//    65      28672       57344        2            0
//    66      32768       32768        1            0
 
? 其中:
 
? class:是class id, 對應了span結構體所屬的class的種類,可以看到一共66中,實際一共67種。大于32K的內存 分配,會直接從mheap中分配,后面會介紹。
 
? bytes/obj:每個對象占用的字節數
 
? bytes/span:每個span的大小,也就是頁數*8k(頁大小)
 
? objects:該類span所擁有的對象數,span所占字節數/對象所占字節數
 
? waste bytes:該類span浪費的字節數,從以上分析可以看出,每一類span并不能剛好按該類對象大小,分配整數個對象,即做到每一字節物盡其用,這個值是:span所占字節數%對象所占字節數
 
? 以class 10 為例,span與管理的內存如下圖所示:
 
 
表示當前span類別屬于class10,大小只有1頁,又切分56個大小為144字節的塊,其中兩個已分配。
 

 mheap

 
? mheap管理整個go程序的堆空間,在源文件runtime/mheap.go中找到了該結構體描述,以及全局變量mheap_,結構體主要字段如下:
 
 
type mheap struct {
   // lock must only be acquired on the system stack, otherwise a g
   // could self-deadlock if its stack grows with the lock held.
   lock      mutex
   pages     pageAlloc // page allocation data structure
   allspans []*mspan // all spans out there
 
   // Malloc stats.
   largealloc  uint64                  // bytes allocated for large objects
   nlargealloc uint64                  // number of large object allocations
   
   central [numSpanClasses]struct {
      mcentral mcentral
      pad      [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte
   }
   spanalloc             fixalloc // allocator for span*
   cachealloc            fixalloc // allocator for mcache*
   curArena struct {
base, end uintptr
}
arenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena
}
 
 
? pages:堆的也分配
 
? allspans:所有分配的span
 
? largealloc:超過32k大對象的分配空間的字節數
 
? nlargealloc:超過32k大對象分配的對象數目
 
? central:mcentral結構單元
 
? spanalloc:mspan分配器
 
? cachealloc:mache分配器
 
? curArena: 當前arena的起始地址
 
? arenas:將虛擬地址空間以arena幀的形式一片片分割
 
? arenas變成了一個heapArea的指針數組。
 
 
type heapArena struct {
   bitmap [heapArenaBitmapBytes]byte
   spans [pagesPerArena]*mspan
   pageInUse [pagesPerArena / 8]uint8
   pageMarks [pagesPerArena / 8]uint8
   zeroedBase uintptr
}
 
 
? 這個結構體描繪了一個arena,查看runtime/malloc.go
 
 
heapArenaBytes = 1 << logHeapArenaBytes
 
// logHeapArenaBytes is log_2 of heapArenaBytes. For clarity,
// prefer using heapArenaBytes where possible (we need the
// constant to compute some other constants).
logHeapArenaBytes = (6+20)*(_64bit*(1-sys.GoosWindows)*(1-sys.GoarchWasm)) + (2+20)*(_64bit*sys.GoosWindows) + (2+20)*(1-_64bit) + (2+20)*sys.GoarchWasm
 
// heapArenaBitmapBytes is the size of each heap arena's bitmap.
heapArenaBitmapBytes = heapArenaBytes / (sys.PtrSize * 8 / 2)
 
pagesPerArena = heapArenaBytes / pageSize
```
 
在64位Linux系統上,單個arena的大小heapArenaBytes是64MB,每個arena分成8k大小的頁。整個虛擬內存的第一級切分圖,如下所示:
 
 
每個heapArena結構體按下圖管理每個arena,
 
 
? bitmap在gc的時候起作用,其中每個字節標識了arena每四個指針大小空間(也就是32字節大小)的內容情況:1位標識是否已掃描,一位標識是否有指針
 
mcentral
 
? 從mheap的結構體中,可以看到,mheap創建了一個包含164個mcentral對象的數組。也就是mheap管理著164個mcentral。mcentral結構體類型如下所示:
 
 
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
}
 
 
? lock:互斥鎖
 
? spanclass:所屬span的類型,從這里可以推斷,應該是每一種類型的span,都有一個對應的mcentral結構體
 
? nonempty:含有空對象且可分配的span列表,查看這個類型,可以發現是個知頭尾的雙向鏈表
 
type mSpanList struct {
   first *mspan // first span in list, or nil if none
   last  *mspan // last span in list, or nil if none
}
 
 
? empty:不含空對象且不可分配的span列表
 
? nmalloc:已分配的累計對象數目
 
? mcentral為所有mcache提供分配好的mspan資源。當某一個P私有的mcache沒有可用的span的以后,會動態的從mcentral申請,之后就會緩存在mcache中。前面介紹到mheap會創建134個mcentral,也就是每個class類型的span會有兩個對應的mcentral:span內包含指針和不包含指針的。
 
? mcentral與mspan的對應關系如下圖所示:
 
? 先簡單總結mcache從mcentral獲取和歸還span:
 
? 獲取:先加鎖,從nonempty鏈表找一個可用的mspan,從該鏈表刪除,并加入到empty鏈表中,然后把mspan返回給當前P中運行的協程,解鎖。
 
? 歸還:先加鎖, 把mspan從empty鏈表刪除,然后加入到nonempty鏈表,解鎖。
 
mcache
 
? mspan作為內存管理的基本單位,顯然需要上一級單位來管理它:mcache。在runtime/mcache.go里面找到了這個結構體,只顯示關鍵字段。這是一個指針數組,再想到mspan結構的類型,可以想到是多條鏈表。
 
 
type mcache struct {
   alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass
 
}
 
 
? 查看這個numSpanClasses,發現值是67<<1,等于134。意味著是上述class分類總數的兩倍,這是為何?原因是:上述的每種claas類型的span都有兩組列表,其中第一組列表中的對象包含了指針,第二組列表中表示的對象不包含指針。這樣做的目的是以空間換時間去提高GC掃描的性能,畢竟不用掃描不帶指針的那一條列表。mcache和span的對應關系如下圖所示:
 
? mcache在初始化的時候是空的,隨著程序的執行,會動態的從central中獲取并緩存下來。查看源碼我們發現,mcache結構體是沒有鎖的,是如何保證多線程安全的?每一個P(goroutine調度的GPM模型參考)都會有自己一個私有的mache,而每次只會有一個協程運行在同一個P上,也就是說每個P都擁有一個本地、私有化的mcache(內存池),所以不用加鎖。
 
小結
 
? 以64位4核處理器的Linux系統為例,4個邏輯處理器的go運行環境配置為例,虛擬內存堆區、mheap、mcentral、mcache和邏輯處理器p及goroutine的關聯關系如下入所示:
 
 
? 1、mheap創建了4M個heapArena結構體,把48位地址線管理的256T地址空間切分成一個個64MB的叫做Arena的塊。同時創建了包含134個元素的mcentral數組,每一個mcentral管理著同屬一類classid的span塊組成的鏈表。而每一類classid的span塊列表又分為帶指針的span塊和不帶指針的span塊,所以67類classid需要134個mcentral來管理。
 
? 2、每個mcentral中有兩個span鏈表:帶空余對象的可分配span鏈表和不帶空余對象或在mcache中已被使用的不可分配span列表。當P向本地的mcache申請span,而得不到時,mcache會向mcentral申請。mcentral為所有P共有,所以需要加鎖。
 
? 3、每一個邏輯P都有獨立的mcache用于緩存該邏輯處理器已申請的span,當有G運行在P上,且要去申請內存時,會優先從與該P對應綁定的mcache中申請,因為在P上同時只會有一個G在運行,且mcache專屬于P,所以不需要加鎖。與mcentral類似,每個mcache針對每個span類型的class維護兩條鏈表:帶指針的span塊和不帶指針的span塊,所以每個mcache中也有134條span塊的鏈表。
 
? 4、根據所管理對象大小,mspan一共被劃分為66類。mspan將分配得到的arena頁再度按所屬種類的對象大小再度切分,以  class類型24為例,占據一頁空間,對象大小為480bytes,因此該span被分為17個大小為480字節的小塊,一共使用8160字節,并有32字節被浪費掉。
 
### 內存分配
 
? 小于16字節的微小對象:
 
? 使用mcache的微小分配器,分配小于16B的對象。
 
? 16B~32KB的小對象:
 
? 由運行G所在P的去對應的mcache中查找對應大小的class,如果mcache分配失敗,則去mcentral中查找,否則再去mheap中申請新的頁用于mspan,并掛在mcentral與mcache中。
 
? 大于32K大對象:
 
? 由mheap直接申請,并分配在保存在mcentral的class0類型中。
0條評論
0 / 1000
羅****藝
3文章數
0粉絲數
羅****藝
3 文章 | 0 粉絲
羅****藝
3文章數
0粉絲數
羅****藝
3 文章 | 0 粉絲
原創

golang內存分配與管理

2023-10-07 08:41:09
17
0

概述

 
? golang的內存分配機制源自Google的tcmalloc算法,英文全稱thread caching malloc,從名字可以看出,是在原有基礎上,針對多核多線程的內存管理進行優化而提出來的。該算法的核心思想是內存的多級管理,進而降低鎖的粒度;將內存按需劃成大小不一的塊,減少內存的碎片化。為每個P,也就是go協程調度模型了里面的邏輯處理器維護一個mcache結構體的獨立內存池,只有當該內存池不足時,才會向全局mcentral和mheap結構體管理的內存池申請。為每一個P維持一個私有的本地內存池,從而不用加鎖,加快了內存分配速度。只有在本地P的內存池被消耗完,或者申請的內存太大時,才會訪問全局的內存池,大大減小了多線程下對全局內存池訪問帶來的競爭系統損耗。
 

內存架構

 
? 以64位的系統為例,1.11版本以后go程序的整個堆內存是連續,如下圖 所示。結構雖然簡單,但是在混合使用go和c的時候會導致程序崩潰,例如分配的內存地址發生沖突,導致初始化堆和擴容失敗。
 
? 自1.11版本以后,對堆實現了分塊處理,arena不再是連續的,以64位的Linux系統為例,是一個塊塊64MB大小的塊。golang內存的三級架構如下圖所示。下面將分別介紹各個層。
 

mspan

 
? mspan結構體是go內存管理的基本單元,定義在runtime/mheap.go中,主要結構體成員如下:
 
//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
 
   startAddr uintptr // address of first byte of span aka s.base()
   npages    uintptr // number of pages in span
   
   nelems uintptr // number of object in the span.
   
   allocBits  *gcBits
   
   allocCount  uint16        // number of allocated objects
   spanclass   spanClass     // size class and noscan (uint8)
   elemsize    uintptr       // computed from sizeclass or from npages
   .....
}
 
? 可以發現,這是一個雙向鏈表。
 
? startAddr:當前span在arena中的起始字節的地址
 
? npages:當前span包含arena中多少頁
 
? nelems:當前span,包含多少個對象。golang又對每一個span,按照所屬class的不同,切分成大小不同的塊,以減少內存碎片。
 
? allocCount:已分配的對象數目
 
? elemsize:對象大小
 
? spanclass:span所屬的class。
 
? 根據對象的大小,golang劃分了一系列的class,以應對各種場景的內存分配,較少內存碎片化。每個class都有一個固定大小的對象和固定的span大小,如下所示:
 
// class  bytes/obj  bytes/span  objects  waste bytes
//     1          8        8192     1024            0
//     2         16        8192      512            0
//     3         32        8192      256            0
//     4         48        8192      170           32
//     5         64        8192      128            0
//     6         80        8192      102           32
//     7         96        8192       85           32
//     8        112        8192       73           16
//     9        128        8192       64            0
//    10        144        8192       56          128
//    11        160        8192       51           32
//    12        176        8192       46           96
//    13        192        8192       42          128
//    14        208        8192       39           80
//    15        224        8192       36          128
//    16        240        8192       34           32
//    17        256        8192       32            0
//    18        288        8192       28          128
//    19        320        8192       25          192
//    20        352        8192       23           96
//    21        384        8192       21          128
//    22        416        8192       19          288
//    23        448        8192       18          128
//    24        480        8192       17           32
//    25        512        8192       16            0
//    26        576        8192       14          128
//    27        640        8192       12          512
//    28        704        8192       11          448
//    29        768        8192       10          512
//    30        896        8192        9          128
//    31       1024        8192        8            0
//    32       1152        8192        7          128
//    33       1280        8192        6          512
//    34       1408       16384       11          896
//    35       1536        8192        5          512
//    36       1792       16384        9          256
//    37       2048        8192        4            0
//    38       2304       16384        7          256
//    39       2688        8192        3          128
//    40       3072       24576        8            0
//    41       3200       16384        5          384
//    42       3456       24576        7          384
//    43       4096        8192        2            0
//    44       4864       24576        5          256
//    45       5376       16384        3          256
//    46       6144       24576        4            0
//    47       6528       32768        5          128
//    48       6784       40960        6          256
//    49       6912       49152        7          768
//    50       8192        8192        1            0
//    51       9472       57344        6          512
//    52       9728       49152        5          512
//    53      10240       40960        4            0
//    54      10880       32768        3          128
//    55      12288       24576        2            0
//    56      13568       40960        3          256
//    57      14336       57344        4            0
//    58      16384       16384        1            0
//    59      18432       73728        4            0
//    60      19072       57344        3          128
//    61      20480       40960        2            0
//    62      21760       65536        3          256
//    63      24576       24576        1            0
//    64      27264       81920        3          128
//    65      28672       57344        2            0
//    66      32768       32768        1            0
 
? 其中:
 
? class:是class id, 對應了span結構體所屬的class的種類,可以看到一共66中,實際一共67種。大于32K的內存 分配,會直接從mheap中分配,后面會介紹。
 
? bytes/obj:每個對象占用的字節數
 
? bytes/span:每個span的大小,也就是頁數*8k(頁大小)
 
? objects:該類span所擁有的對象數,span所占字節數/對象所占字節數
 
? waste bytes:該類span浪費的字節數,從以上分析可以看出,每一類span并不能剛好按該類對象大小,分配整數個對象,即做到每一字節物盡其用,這個值是:span所占字節數%對象所占字節數
 
? 以class 10 為例,span與管理的內存如下圖所示:
 
 
表示當前span類別屬于class10,大小只有1頁,又切分56個大小為144字節的塊,其中兩個已分配。
 

 mheap

 
? mheap管理整個go程序的堆空間,在源文件runtime/mheap.go中找到了該結構體描述,以及全局變量mheap_,結構體主要字段如下:
 
 
type mheap struct {
   // lock must only be acquired on the system stack, otherwise a g
   // could self-deadlock if its stack grows with the lock held.
   lock      mutex
   pages     pageAlloc // page allocation data structure
   allspans []*mspan // all spans out there
 
   // Malloc stats.
   largealloc  uint64                  // bytes allocated for large objects
   nlargealloc uint64                  // number of large object allocations
   
   central [numSpanClasses]struct {
      mcentral mcentral
      pad      [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte
   }
   spanalloc             fixalloc // allocator for span*
   cachealloc            fixalloc // allocator for mcache*
   curArena struct {
base, end uintptr
}
arenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena
}
 
 
? pages:堆的也分配
 
? allspans:所有分配的span
 
? largealloc:超過32k大對象的分配空間的字節數
 
? nlargealloc:超過32k大對象分配的對象數目
 
? central:mcentral結構單元
 
? spanalloc:mspan分配器
 
? cachealloc:mache分配器
 
? curArena: 當前arena的起始地址
 
? arenas:將虛擬地址空間以arena幀的形式一片片分割
 
? arenas變成了一個heapArea的指針數組。
 
 
type heapArena struct {
   bitmap [heapArenaBitmapBytes]byte
   spans [pagesPerArena]*mspan
   pageInUse [pagesPerArena / 8]uint8
   pageMarks [pagesPerArena / 8]uint8
   zeroedBase uintptr
}
 
 
? 這個結構體描繪了一個arena,查看runtime/malloc.go
 
 
heapArenaBytes = 1 << logHeapArenaBytes
 
// logHeapArenaBytes is log_2 of heapArenaBytes. For clarity,
// prefer using heapArenaBytes where possible (we need the
// constant to compute some other constants).
logHeapArenaBytes = (6+20)*(_64bit*(1-sys.GoosWindows)*(1-sys.GoarchWasm)) + (2+20)*(_64bit*sys.GoosWindows) + (2+20)*(1-_64bit) + (2+20)*sys.GoarchWasm
 
// heapArenaBitmapBytes is the size of each heap arena's bitmap.
heapArenaBitmapBytes = heapArenaBytes / (sys.PtrSize * 8 / 2)
 
pagesPerArena = heapArenaBytes / pageSize
```
 
在64位Linux系統上,單個arena的大小heapArenaBytes是64MB,每個arena分成8k大小的頁。整個虛擬內存的第一級切分圖,如下所示:
 
 
每個heapArena結構體按下圖管理每個arena,
 
 
? bitmap在gc的時候起作用,其中每個字節標識了arena每四個指針大小空間(也就是32字節大小)的內容情況:1位標識是否已掃描,一位標識是否有指針
 
mcentral
 
? 從mheap的結構體中,可以看到,mheap創建了一個包含164個mcentral對象的數組。也就是mheap管理著164個mcentral。mcentral結構體類型如下所示:
 
 
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
}
 
 
? lock:互斥鎖
 
? spanclass:所屬span的類型,從這里可以推斷,應該是每一種類型的span,都有一個對應的mcentral結構體
 
? nonempty:含有空對象且可分配的span列表,查看這個類型,可以發現是個知頭尾的雙向鏈表
 
type mSpanList struct {
   first *mspan // first span in list, or nil if none
   last  *mspan // last span in list, or nil if none
}
 
 
? empty:不含空對象且不可分配的span列表
 
? nmalloc:已分配的累計對象數目
 
? mcentral為所有mcache提供分配好的mspan資源。當某一個P私有的mcache沒有可用的span的以后,會動態的從mcentral申請,之后就會緩存在mcache中。前面介紹到mheap會創建134個mcentral,也就是每個class類型的span會有兩個對應的mcentral:span內包含指針和不包含指針的。
 
? mcentral與mspan的對應關系如下圖所示:
 
? 先簡單總結mcache從mcentral獲取和歸還span:
 
? 獲取:先加鎖,從nonempty鏈表找一個可用的mspan,從該鏈表刪除,并加入到empty鏈表中,然后把mspan返回給當前P中運行的協程,解鎖。
 
? 歸還:先加鎖, 把mspan從empty鏈表刪除,然后加入到nonempty鏈表,解鎖。
 
mcache
 
? mspan作為內存管理的基本單位,顯然需要上一級單位來管理它:mcache。在runtime/mcache.go里面找到了這個結構體,只顯示關鍵字段。這是一個指針數組,再想到mspan結構的類型,可以想到是多條鏈表。
 
 
type mcache struct {
   alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass
 
}
 
 
? 查看這個numSpanClasses,發現值是67<<1,等于134。意味著是上述class分類總數的兩倍,這是為何?原因是:上述的每種claas類型的span都有兩組列表,其中第一組列表中的對象包含了指針,第二組列表中表示的對象不包含指針。這樣做的目的是以空間換時間去提高GC掃描的性能,畢竟不用掃描不帶指針的那一條列表。mcache和span的對應關系如下圖所示:
 
? mcache在初始化的時候是空的,隨著程序的執行,會動態的從central中獲取并緩存下來。查看源碼我們發現,mcache結構體是沒有鎖的,是如何保證多線程安全的?每一個P(goroutine調度的GPM模型參考)都會有自己一個私有的mache,而每次只會有一個協程運行在同一個P上,也就是說每個P都擁有一個本地、私有化的mcache(內存池),所以不用加鎖。
 
小結
 
? 以64位4核處理器的Linux系統為例,4個邏輯處理器的go運行環境配置為例,虛擬內存堆區、mheap、mcentral、mcache和邏輯處理器p及goroutine的關聯關系如下入所示:
 
 
? 1、mheap創建了4M個heapArena結構體,把48位地址線管理的256T地址空間切分成一個個64MB的叫做Arena的塊。同時創建了包含134個元素的mcentral數組,每一個mcentral管理著同屬一類classid的span塊組成的鏈表。而每一類classid的span塊列表又分為帶指針的span塊和不帶指針的span塊,所以67類classid需要134個mcentral來管理。
 
? 2、每個mcentral中有兩個span鏈表:帶空余對象的可分配span鏈表和不帶空余對象或在mcache中已被使用的不可分配span列表。當P向本地的mcache申請span,而得不到時,mcache會向mcentral申請。mcentral為所有P共有,所以需要加鎖。
 
? 3、每一個邏輯P都有獨立的mcache用于緩存該邏輯處理器已申請的span,當有G運行在P上,且要去申請內存時,會優先從與該P對應綁定的mcache中申請,因為在P上同時只會有一個G在運行,且mcache專屬于P,所以不需要加鎖。與mcentral類似,每個mcache針對每個span類型的class維護兩條鏈表:帶指針的span塊和不帶指針的span塊,所以每個mcache中也有134條span塊的鏈表。
 
? 4、根據所管理對象大小,mspan一共被劃分為66類。mspan將分配得到的arena頁再度按所屬種類的對象大小再度切分,以  class類型24為例,占據一頁空間,對象大小為480bytes,因此該span被分為17個大小為480字節的小塊,一共使用8160字節,并有32字節被浪費掉。
 
### 內存分配
 
? 小于16字節的微小對象:
 
? 使用mcache的微小分配器,分配小于16B的對象。
 
? 16B~32KB的小對象:
 
? 由運行G所在P的去對應的mcache中查找對應大小的class,如果mcache分配失敗,則去mcentral中查找,否則再去mheap中申請新的頁用于mspan,并掛在mcentral與mcache中。
 
? 大于32K大對象:
 
? 由mheap直接申請,并分配在保存在mcentral的class0類型中。
文章來自個人專欄
文章 | 訂閱
0條評論
0 / 1000
請輸入你的評論
0
0