golang内存管理简介


go runtime抛弃了传统的内存分配方式,改为自主管理。其内存分配算法主要源自 Google为C语言开发的 TCMalloc算法。其核心思想是把内存分为多级管理,从而降低锁的粒度

基础概念

go维护着一块大的全局内存;每个调度器(P,Processer)维护一块小的私有内存,不足时再从全局申请。
mem-allocate

预申请的内存分为:

  • spans:存放span的指针(指向对应page),共需要(512G/8KB)*8byte=512M(8KB为page大小,8byte为指针大小);
  • bitmap:主要用于GC(垃圾回收);
  • arena:即堆区(共512G,按8K划分为page);

span

span是用于管理arena页的关键数据结构,是内存管理的基本单位,每个span包含一个或多个连续页:

  • 为满足小对象分配,span中一页会划分为更小粒度;
  • 大对象(超过页大小),则通过多页实现;

每个span用于管理特定的class对象,对象class大小及与span间关系如下图所示:
span-class-map

  • class:每个span结构中都有一个class ID,表示此span可处理的对象类型;
  • bytes/obj:每个class对应对象的字节数;
  • bytes/span:每个span占用堆的字节数;
  • objects:每个span可分配对象数;
  • waste bytes:每个span的内存碎片(及浪费的内存);

span中最大对象是32K,超过此大小的由特殊class(class ID为0)表示。

span结构类似:

type mspan struct {
	next *mspan //链表前向指针, 用于将span链接起来
	prev *mspan //链表前向指针, 用于将span链接起来
	startAddr uintptr // 起始地址, 也即所管理页的地址
	npages uintptr // 管理的页数

	nelems uintptr // class对象个数,即可分配出多少个对象

	allocBits *gcBits //分配位图, 每一位代表一个块是否已分配

	allocCount uint16 // 已分配块的个数
	spanclass spanClass // class表中的class ID

	elemsize uintptr // class表中的对象大小, 也即块大小
}

span管理内存如下所示(里面包含class标识与大小,以及分配的数量):
span-mem-alloc

cache

管理span的数据结构为mcentral,各线程从mcentral管理的span中申请内存;为避免申请内存时加锁,golang为每个线程分配了span的缓存(cache)。

type mcache struct {
	alloc [67*2]*mspan // 按class分组的mspan列表
}

每个class类型都有两组span列表(为提高GC扫描性能:不包含指针的span列表不需要扫描):

  • 第一组:表示的对象包含了指针;
  • 第二组:表示的对象不含指针;

根据对象是否包含指针,划分为noscan与scan两类
在这里插入图片描述

mchache在初始化时没有任何span,在使用过程中动态从central中获取并缓存。

central

central是全局资源,为多个线程服务(线程内存不足时,向central申请;当线程释放内存时,再放回central)

type mcentral struct {
	lock mutex //互斥锁
	spanclass spanClass // span class ID
	nonempty mSpanList // non-empty 指还有空闲块的span列表
	empty mSpanList // 指没有空闲块的span列表

	nmalloc uint64 // 已累计分配的对象个数
}

线程从central获取span步骤:

  • 加锁
  • 从nonempty链表中获取可用span,并从链表中删除;
  • 将取出的span放入empty链表;
  • 将span返回给线程;
  • 解锁;
  • 线程将span缓存进cache;

heap

mheap代表go程序持有的所有堆空间,golang通过一个mheap全局变量进行内存管理:
在这里插入图片描述

mcentral没有空闲的mspan时,会向mheap申请。而mheap没有资源时,会向操作系统申请新内存。mheap主要用于大对象的内存分配,以及管理未切割的mspan,用于给mcentral切割成小对象。

mheap中含有所有规格的mcentral,所以,当一个mcachemcentral申请mspan时,只需要在独立的mcentral中使用锁,并不会影响申请其他规格的mspan

分配过程

针对不同的对象有不同的分配逻辑:

  • (0,16B)且不包含指针的对象:Tiny分配;
  • (0,16B)包含指针的对象:正常分配;
  • [16B,32KB]:正常分配;
  • (32KB, -):大对象分配。

分配size为n的对象步骤:

  • 获取当前线程私有缓存mcache;
  • 根据size计算出合适的classID;
  • 从mcache的alloc[class]链表中查询可用span;
  • 若mcache中无可用span,则从mcentral中申请一个新的span放入mcache中;
  • 若mcentral中也无可用span,则从mheap中申请一个新的span加入到mcentral中;
  • 从span中获取空闲对象地址并返回;
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值