golang作为一种“高级语言”,也提供了自己的内存管理机制。这样一方面可以简化编码的流程,降低因内存使用导致出现问题的频率(C语言使用者尤其是初学者应该深有体会),对程序猿友好。另一方面也可以减少内存相关系统调用,提升性能。
先了解下内存管理大致策略:
申请一块较大的地址空间(虚拟内存),用于内存分配及管理(golang:spans+bitmap+arena->512M+16G+512G)
当空间不足时,向系统申请一块较大的内存,如100KB或者1MB
申请到的内存块按特定的size,被分割成多种小块内存(golang:_NumSizeClasses = 67),并用链表管理起来
创建对象时,按照对象大小,从空闲链表中查找到最适合的内存块
销毁对象时,将对应的内存块返还空闲链表中以复用
空闲内存达到阈值时,返还操作系统
以下,基于go1.9版本,看下golang内存分配实现的基本思路。
Go内存管理的实现go的内存管理实现基于TCMalloc(Thread-Caching Malloc)。
TCMalloc是 Google 开发的多级内存分配器,具有对抗内存碎片化,适合高并发场景的特性。据称,它的内存分配速度是 glibc2.3 中实现的 malloc的数倍。
和TCMalloc相同,go的内存分配也是基于两种粒度的内存单位:span和object。span是连续的page,按page的数量进行归类,比如分为2个page的span,4个page的span等。object是span中按预设大小划分的块,也是按大小分类。同一个span中,只有一种类型(大小)的object。
go内存分配主要有三个管理组件:
mcache
Per-P(Processer,具体参见go中G,M,P的概念)私有cache,用于实现无锁的object分配
mcentral
全局内存,为各个cache提供按大小划分好的span
mheap
全局内存,page管理,内存不足时向系统申请
通过将内存分配流程分为三个层级,既能保证Processer级别(mcache)的无锁分配,又能在mcentral级别实现内存全局共享,避免浪费。
go将内存申请按大小分为三种类型:tiny,small,large。tiny是小于16个byte的申请,small是小于32KB的申请,大于32KB为large,三种类型的处理方式有所不同。
_TinySize = 16 _MaxSmallSize = 32768