Go Memory
TCMalloc 给每个线程都分配一个局部缓存,采用线程局部数据技术。小块内存的分配从线程局部缓存分配即可满足。同时周期性地将线程缓存内存回收到中心缓存中,线程局部缓存不足时从中心缓存中申请。 小对象分配(<=256K) 对于256K以内的内存大小,划分了约88个种类,每个size class对应一个种类,分配时是将申请内存大小向上取整到size class类别的大小,比如申请5bytes会分配8bytes,由于分配的内存会大于申请的内存,则会造成内存浪费。 每个线程分配单独的缓存ThreadCache,ThreadCache中对于每个size class都分配一个FreeList用于分配和回收该大小的内存空间。小对象的分配直接在ThreadCache中对应大小的FreeList中获取空闲对象 全局共享缓存CentralCache,对于每个size class都分配一个CentralFreeList,当ThreadCache尝试分配发现没有空闲对象时,就从CentralCache的对应大小的CentralFreeList中获取一部分空闲对象,这里需要加锁。 当CentralCache中的CentralFreeList没有空闲对象时,向PageHeap申请内存,将其划分成对应大小的空闲对象,加入到空闲链表中。 内存回收时,将空闲对象插入对应的ThreadCache的FreeList中,当满足一定条件将FreeList中的内存归还给CentralCache,当满足一定条件将CentralCache的CentralFreeList中的内存归还给PageHeap 中对象分配(256K<size<=1M) 对于中对象的分配,从PageHeap中的FreeLsit中获取,PageHeap划分为128个span,每个span由1,2…128个page组成。 分配内存时首先向上取整到对应K个page 从PageHeap的对应大小的FreeList中尝试获取span 如果该大小没有空闲的span,则增大page往下寻找 找个n个page的span后,将该span划分为两部分 将k个page的span作为结果返回 将剩余的n-k个page的span,插入到PageHeap中 如果128个spanlist都没有空闲对象,则当做大对象来分配 大对象分配(>1M) 对于大于1m的对象或者小于1m但PageHeap的spanList无法满足的对象,则采用大对象分配。 首先向上取整到对应K个page 大于128K的span都是使用红黑树来保存的,然后使用best-fit首次适应算法,找到合适的span 找个n个page的span后,将该span划分为两部分 将k个page的span作为结果返回 将剩余的n-k个page的span,如果n-k>128则插入红黑树中继续用于大对象的分配,n-k<=128则插入到PageHeap中 如果无法找到足够大小的空闲对象,则需要向系统申请新的内存 内存分配 分级分配 golang根据对象内存大小划分为3类, 微对象:0~16B 小对象:16B~32KB,以及<16B的指针对象 大对象:32KB以上 三级内存管理 线程缓存:每个线程有自己的内存缓存,如果内存大小能够满足,则直接在线程缓存上分配,不需要考虑全局锁的问题 中心缓存:当线程缓存无法满足时,就需要在全局的中心缓存来分配 页堆:当需要分配32KB以上的大对象时,使用页堆 内存管理 内存管理单元mspan 每个mspan都管理npages个8K的页 比如最小的分配8B的mspan,则需要一个8K的页,因此最后可以分配8K/8B=1024个对象 比如最大的分配32K的mspan,则需要4个8K的页,因此最多可以分配32K/32K=1个对象 mspan中使用allocBits来标记内存的占用情况,1表示已分配,0表示未使用 mspan分为无指针的noscan和有指针的两种,与gc有关 线程缓存mcache 每个P都有自己的mcache,用于分配小对象 每个mcache都有alloc [numSpanClasses]*mspan,67*2=134个mspan,用来分配不同大小的对象 每个P在初始化时会调用allocmcache来初始化mcache 初始化时mcache中每个mspan都是空的emptymspan,并没有内存空间 当mcache中的mspan满或者为emptymspan时,调用refill从中心缓存中获取至少包含一个空闲对象空间的mspan 中心缓存mcentral 每个spanclass对应一个mcentral,共134个 mcentral中主要维护两个spanlist nonempty mSpanList:表示存在空闲对象空间的mspan empty mSpanList:表示没有空闲对象空间的mspan或者缓存在mcache中 主要方法cacheSpan该方法来返回span给mcache // Allocate a span to use in an mcache. func (c *mcentral) cacheSpan() *mspan { sg := mheap_....