学习了上2篇Go语言的内存管理的原理(一、TCMalloc原理https://juejin.cn/post/6919005994966056968 二、Go内存管理原理https://juejin.cn/post/6920485540391288839)我们理解源码的流程就非常easy啦。
一、结构体分析
首先来看一下,mcache,mcentral,mheap这三个结构体。我们选则最长使用的字段进行分析。注:这里都是基于go1.14源码分析(对照代码看体验更佳
1 | // mcache |
二、不同对象的内存申请流程
接下来我们按照Tiny对象,小对象,大对象分类来介绍内存分配的流程。
0、newobject()接口
对象的内存分配都在newobject()接口,这个接口包括内存申请和回收。本文主要学习内存申请源码
1 | func newobject(typ *_type) unsafe.Pointer { |
1、Tiny对象内存申请(<16B)
1 | func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { |
若本地的tiny内存块不足,则向mcache继续进行申请
1 | // 在mache申请空闲内存 |
总结一下,Tiny对象的申请(小于16B)若当前Tiny分配器本地有足够的内存,则在本地申请;否则向mcache申请sizeclass=5的sapn,若mcache没有对应级别的span,则向mcentral申请。
2、小内存申请(< 32KB)
1 | func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { |
若cache没有对应的span,使用nextFreeFast(span)接口向central申请,该接口已经在Tiny对象内存申请介绍过。
在nextFreeFast(span)接口中主要使用refill接口
1 | func (c *mcache) refill(spc spanClass) { |
cacheSpan是向mcentral申请span的核心接口
1 | func (c *mcentral) cacheSpan() *mspan { |
小对象是在mache申请适合自己大小的span,若mache没有可用的span,mache会向mcentral申请,加锁,找一个可用的span,从nonempty删除该span,然后放到empty链表中,将span返回给工作线程,解锁;若没有足够的内存,mcentral还会继续向mheap继续申请。
3、大对象内存申请
大对象直接在mheap进行申请
1 | func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { |
使用classsize=0进行申请
1 | // 大对象申请 |
向mheap申请一个span
1 | func (h *mheap) alloc(npages uintptr, spanclass spanClass, needzero bool) *mspan { |
1 | func (h *mheap) allocSpan(npages uintptr, manual bool, spanclass spanClass, sysStat *uint64) (s *mspan) { |
从页缓存申请,只能申请连续个页,若不能连续则返回失败
1 | // 申请pageCache |
1 | func (c *pageCache) allocN(npages uintptr) (uintptr, uintptr) { |
这里插播一下pcache
的原理
go1.14中使用bitmap来管理内存页,并在每个P中维护一份pageCache。pageCache是一个位图,1表示未分配,0表示已分配
1 | type pageCache struct { |
一位表示一页(8KB),所以最大表示8KB*64=512KB缓存。当需要分配的页数小于 512/4=128KB时,需要首先从cache中分配
若页缓存没有足够的页,则向虚拟内存申请页
1 | // 在heap进行页申请 |
至此大对象的内存申请已经结束,对于大对象,使用mheap直接分配,若mheap没有足够的内存,则mheap向虚拟内存申请若干个pages。可以看到,约到后面申请内存的代价就越来越大
从源码分析,我们可以学习到
1、相比与之前的go版本使用树来管理内存块,go1.14使用bit位更为高效,管理也很方便,在源码会看到各种位操作也可以达到相同的效果,看的时候直呼:妙啊 (基础差的还得自己算一算
2、看到多处使用空间换时间的操作,都是很大情况下提供了效率
下一篇,我们将共同学习一下GC是什么
引用