记杂||go的内存分配管理
本文类似于个人学习笔记不会面面俱到,需要读者掌握一定基本知识。
先从span讲起,span是go内存的最小管理单位,最小存储单位page为8KB,span便是page的整数倍大小,从8B到32KB共67个等级+0级(我所学习的教材写成了67种,后来发现他写错了,源码部分在runtime/sizeclasses.go 内有详细数据),其中0级代表大小不确定的等级。span的大小和等级并不相同,比如class1 span为8B而span的大小为8KB,也就是说能装下1024个大小为8B的对象。接下来的内容便围绕着span展开:
go使用了一种现代化的内存分配算法TCMalloc,在此基础上进行了三级管理:mcache,mcentral,mheap。
- mcache:中内含 (67种span+1)*(有无指针2种,GC时使用) 共136个span,span为每个P独占,由于P上只会有一个G在运行所以无需加锁,同时也意味着mcache的数量和P相等(注:在初始化时并没有任何span而是在使用时向mcentral申请并缓存)。
- mcentral:为链表结构共 两条(空闲列表和无空闲)*67种 每一级别的span各有一个对应的mcentral,mcentral内span的数量并不固定,如果不够用了则会向mheap申请,全局P共享。
- mheap:全局内存池,便是go运行时向操作系统申请的一大块内存,这里你可能有个误区,栈在什么地方分配内存?没错也在mheap,与java不同,栈的内存也是分配在堆上的。
为什么要这么分?先看go是怎么为对象分配内存的,go的对象分为了3种:微小对象<16B,16B<小对象<32KB,大对象>32KB。这里微小对象和小对象在span中便有足够大的空间分配,所以分配在mcahe中,其中微小对象会放在span的class 2(16B)中,根据2,4,8规则进行字节对齐,这是一种优化手段可以有效减少堆大小,甚至8B的对象并不会放在class 1中,优先把2*8B放在class 2中以减少位图等用于记录的数据。而mcache中如果span满了,便会去向对应mcentral中申请(加锁)(2条链表都遍历,因为由于GC无法保证无空闲链表中是否已经出现了空闲的)新的span,而原来的会标记为空闲,放在mcentral的空闲列表中等待GC,如果mcentral也没有可用的了,便会向mheap申请(全局锁)。而大对象便直接在mheap中进行内存分配。
~飞天大糙
Lotalot你干了什么?!没有golang八股文我们如何抗衡双招,Lotalot淡笑一声:“很简单,我自己写不就是了”说完,他气息终于不再掩饰,显露而出,Go八股文小解!