前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go内存管理详解

go内存管理详解

原创
作者头像
调不完的9527
发布2022-08-19 14:58:47
9180
发布2022-08-19 14:58:47
举报
文章被收录于专栏:go探索go探索

堆内存

1.golang堆内存分配采用和tcmalloc内存分配器类似的算法

2.堆内存划分为一个个arena空间,arena的初始地址记录在arenaBaseOffset中,在amd64架构的linux中,其值默认为64M,每个arena中有8192个page,每个page有8KB。

3.golang将内存默认分为68种大小规格,最小为8B,最大为32KB,大于32的独立分给一种类型(0),同一种规格又区分为可扫描和不可扫描(标量和指针),所有总共有136种mspan。

4.一个arena划分为多个span,一个span包含1到多个page,并固定划分为某种规格的内存块。

mheap

mheap用于管理整个堆内存,一个arena对应一个heapArena结构,一个span对应一个mspan结构。通过它们可以知道某个内存块是否已分配;已分配的内存用作指针还是标量;是否已被GC标记;是否等待清扫等信息.

central

mheap中有一个全局的mspan管理中心---mheap.central,是一个长度为136(68*2)的数组,数组结构是一个mcentral结构 + padding,其作用是为了方便取用各种规格的mspan。

mcentral

一个mcentral对应一种mspan类型,记录在spanclass中,spanclass的结构:

full和partial分别表示已用尽和未用尽,而每个结构里面包含两个并发安全的spanSet,分别表示已清扫和未清扫。

为了降低多个P之间的竞争,所有对象都会有自己的本地小对象缓存mcache。

mcache中存在tiny和alloc结构,tiny用于分配小于16B的对象,alloc是一个长度为136的数组,数组元素是mspan结构。当本地mspan没有空余的时候,回去向全局的mcentral中申请(partial)新的mspan。将已经用尽的mspan替换到full中。

class对应表格

代码语言:javascript
复制
// 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 ID, 表示该span可处理的对象类型
  • bytes/obj:该class代表对象的字节数
  • bytes/span:每个span占用堆的字节数,也即页数*页大小
  • objects: 每个span可分配的对象个数,也即(bytes/spans)/(bytes/obj)
  • waste bytes: 每个span产生的内存碎片,也即(bytes/spans)%(bytes/obj)

超过32K大小的对象class为0。

HeapArena结构

一个arena对应一个heapArena结构

代码语言:javascript
复制
type heapArena struct {
  bitmap       [heapArenaBitmapBytes]byte
  spans        [pagesPerArena]*mspan
  pageInUse    [pagesPerArena / 8]uint8
  pageMarks    [pagesPerArena / 8]uint8
  pageSpecials [pagesPerArena / 8]uint8
  checkmarks   *checkmarksMap
  zeroedBase   uintptr
}

bitmap

用一字节标记arena中4个指针大小的内存空间:低4位用于标记指针/标量;高4位用于标记扫描/终止(后续单元是否包含指针)

spans

大小为8192,每一个index对应一个page,用于确定某一个Page对应的mspan是什么

pageInUse

长度为1024字节(8192位),标记处于使用状态的span的第一个page。(通过它可以知道有几个span,每个span的页数)

pageMarks

标记每个span的第一个page,在GC标记阶段会修改这个位图,标记哪些span中存在被标记的对象;在GC清扫阶段会根据这个位图,来释放不含标记对象的span。

mspan

一个span对应一个mspan结构,用于管理span中的一组连续的page。

代码语言:javascript
复制
type mspan struct {
	next           *mspan
	prev           *mspan
	....
	freeindex      uintptr // 下一个空闲的内存块地址
	nelems         uintptr // 当前mspan的内存块个数
	....
    // allocBits is a bitmap of objects in this span.
    // If n >= freeindex and allocBits[n/8] & (1<<(n%8)) is 0
    // then object n is free;
	allocBits      *gcBits(unit8)	// 标记哪些内存块被使用(分配)了
	gcmarkBits     *gcBits(unit8)	// gc标记位图
	....
	spanclass      spanClass // 同mcentral,当前span的数据格式
	state          mSpanStateBox // 表示此mspan的类型 1表示堆内存,2表示栈内存
    elemsize       uintptr // class表中的对象大小,也即块大小
	....
}

allocBits:是一个uint8类型,用位图标记哪些内存已经被分配使用了(他是数组的首地址)。

mallocgc函数

1.辅助GC

当GC标记速率小于堆内存申请速率时,会要求当前Go携程执行辅助GC工作,每次执行至少标记64KB的内存。辅助标记的内存大小会成为信用额度,后面在申请小于该内存时,不会再执行辅助GC。 对于特殊Go携程,可以窃取全局的信用额度,而逃避辅助GC。

2.空间分配

tiny(小于16kB && noscan) 使用P自身的tiny分配,不够向mcache拿,在没有向mcentral中再拿

normal([16kB,32KB]) mspan分配

large (> 32kB) 直接向mheap(堆内存)申请对应页数page

3.位图标记 如何通过堆内存地址(p),找到对应的mspan进行标记回收?

1.amd64上面linux最多有4096个arena,寻找p在第几个arena?

arenaBaseOffset是arena的起始地址,heapArenaBytes是每个arena的大小。

即 arenaIndex = (p-arenaBaseOffset) / heapArenaBytes

2.如何确定当前p属于哪个page?

找到arena之后,需要确定当前p属于arena中哪一页。

pagesPerArena是每个arena中拥有的page数量,pageSize每一页的大小

pageIndex = (p / pageSize) % pagesPerArena

4.收尾工作

如果处于GC标记阶段,就需要对新分配的对象进行标记(GC屏障机制),如果达到GC触发条件,还需要执行一次GC标记。

栈内存

栈内存同堆内存相似,栈内存也使用mspan来管理内存,只是mspan.state来区分此mspan是栈还是堆。

栈内存在初始化时会使用两个全局的栈分配对象:stackpool 和 stackLarge。

小于32KB的内存由stackpool进行分配

>=32KB的内存由stackLarge进行分配

stackpool

代码语言:javascript
复制
var stackpool [_NumStackOrders]struct {
	item stackpoolItem
	_    [cpu.CacheLinePadSize - unsafe.Sizeof(stackpoolItem{})%cpu.CacheLinePadSize]byte
}

type stackpoolItem struct {
 mu mutex
 span mSpanList
}

stackpool在linux被分成 2KB 4KB 8KB 16KB 的数组(2的幂次方)

stackLarge

代码语言:javascript
复制
var stackLarge struct {
	lock mutex
	free [heapAddrBits - pageShift]mSpanList // free lists by log_2(s.npages)
}

stackLarge中free 是一个[25]mSpanList的数组,他的第0和1位是不会使用的,从8KB开始,下一位是上一位的两倍,是为了方便使用 log2(index)=pageNum。

分配顺序和堆内存相似

<32KB时 本地缓存(P)的 stackcache -> 全局的stackpool ->全局堆内存中申请

>=32KB时 计算所需要的Pgae数目,然后利用上诉公式,在stackLarge中找到对应的index,获取一个mspan

栈增长

栈增长是成倍增长,基础为2KB,每次增长为前一次的两倍,同时将状态置为_Gcopystack,然后调用 copystack 将原本数据复制到新的栈空间,再释放旧的栈空间。

栈收缩

栈收缩发生在GC阶段,最小只会缩到2KB(栈初始大小),可以安全收缩时,则会马上执行栈收缩,否则会设置栈收缩标记:preemptShrink = true,在携程让出cpu时,会检测此参数,进行栈收缩操作。

栈释放

栈释放发生在协程栈运行结束的时候。

>=32KB的栈,如果在GC清扫结算,直接返回给堆内存,否则归还给stackLarge

<32KB的栈,优先归还本地stackcache,如果本地满了归还全局stackpool,再满了就归还到全局堆内存中

引用:

https://www.bilibili.com/video/BV1av411G7pB?spm_id_from=333.999.0.0

https://www.bookstack.cn/read/GoExpertProgramming/chapter04-4.1-memory_alloc.md

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 堆内存
    • mheap
      • central
      • mcentral
      • class对应表格
    • HeapArena结构
      • bitmap
      • spans
      • pageInUse
      • pageMarks
      • mspan
    • mallocgc函数
      • 1.辅助GC
      • 2.空间分配
      • 3.位图标记 如何通过堆内存地址(p),找到对应的mspan进行标记回收?
      • 4.收尾工作
  • 栈内存
    • stackpool
      • stackLarge
        • 栈增长
          • 栈收缩
            • 栈释放
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档