zoukankan      html  css  js  c++  java
  • golang 学习笔记 ---内存分配与管理

    Go语言——内存管理

    参考:

    图解 TCMalloc

    Golang 内存管理

    Go 内存管理

    问题

    1. 内存碎片:避免内存碎片,提高内存利用率。
    2. 多线程:稳定性,效率问题。

    内存分配

     
    内存划分
    • arena即为所谓的堆区,应用中需要的内存从这里分配, 大小为512G,为了方便管理把arena区域划分成一个个的page,每个page为8KB,一共有512GB/8KB个页
    • spans区域存放span的指针,每个指针对应一个page,所以span区域的大小为(512GB/8KB) * 指针大小8byte = 512M
    • bitmap区域大小也是通过arena计算出来512GB / (指针大小(8 byte) * 8 / 2) = 16G,用于表示arena区域中哪些地址保存了对象, 并且对象中哪些地址包含了指针,主要用于GC。

    分配细节

    1. object size > 32K,则使用 mheap 直接分配。
    2. object size < 16 byte,不包含指针使用 mcache 的小对象分配器 tiny 直接分配;包含指针分配策略与[16 B, 32 K]类似。
    3. object size >= 16 byte && size <=32K byte 时,先使用 mcache 中对应的 size class 分配。
    4. 如果 mcache 对应的 size class 的 span 已经没有可用的块,则向 mcentral 请求。
    5. 如果 mcentral 也没有可用的块,则向 mheap 申请,并切分。
    6. 如果 mheap 也没有合适的 span,则向操作系统申请。

    span

    可以看出span是一个非常重要的数据结构,每个span包含若干个连续的page。

    小对象分配会在span page中划分更小的粒度;大对象通过多页实现。

    size class

    go1.10src untimesizeclasses.go

    // class  bytes/obj  bytes/span  objects  tail waste  max waste
    //     1          8        8192     1024           0     87.50%
    //     2         16        8192      512           0     43.75%
    //     3         32        8192      256           0     46.88%
    //     4         48        8192      170          32     31.52%
    //     5         64        8192      128           0     23.44%
    //     6         80        8192      102          32     19.07%
    //     7         96        8192       85          32     15.95%
    //     8        112        8192       73          16     13.56%
    //     9        128        8192       64           0     11.72%
    //    10        144        8192       56         128     11.82%
    
    //    ...
    //    65      28672       57344        2           0      4.91%
    //    66      32768       32768        1           0     12.50%
    
    

    上表中每列含义如下:

    • class: class ID,每个span结构中都有一个class ID, 表示该span可处理的对象类型
    • bytes/obj:该class代表对象的字节数
    • bytes/span:每个span占用堆的字节数,也即页数*页大小
    • objects: 每个span可分配的对象个数,也即(bytes/spans)/(bytes/obj)
    • tail bytes: 每个span产生的内存碎片,也即(bytes/spans)%(bytes/obj)

    上表可见最大的对象是32K大小,超过32K大小的由特殊的class表示,该class ID为0,每个class只包含一个对象。所以上面只有列出了1-66。

    有点像装箱算法,按照规格分配,减少内存碎片。

    struct

    span是内存管理的基本单位,每个span用来管子特定的size class对象,根据size class,span将若干个页分成块进行管理。

    go1.10src untimemheap.go

    type mspan struct {
        next *mspan     // next span in list, or nil if none
        prev *mspan     // previous span in list, or nil if none
       
        startAddr uintptr // address of first byte of span aka s.base()
        npages    uintptr // number of pages in span
        
        nelems uintptr // number of object in the span.
        
        allocBits  *gcBits
        gcmarkBits *gcBits
        
        allocCount  uint16     // number of allocated objects
        spanclass   spanClass  // size class and noscan (uint8)
        
        elemsize    uintptr    // computed from sizeclass or from npages
    }
    
     
    10

    以size class 10为例,npages=1,nelems=56,spanclass=10,elemsize=144;startAddr指arena区位置;next和prev指spans区,span链表;allocBits是一个bitmap,标记分配块分配情况,这个设计我也用过,之前用redis bitmap实现了IPAM。

    cache

    从上面我们知道go通过span来分配内存,那在哪里用span?通过之前的学习Go语言——goroutine并发模型,我们知道每个P都有mcache,通过mcache管理每个G需要的内存。

    go1.10src untimemcache.go

    type mcache struct {
       tiny             uintptr
       tinyoffset       uintptr
        
       alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass
    }
    
    numSpanClasses = _NumSizeClasses << 1
    _NumSizeClasses = 67
    

    alloc是span数组,长度是67 << 1,说明每种size class有2组元素。第一组span对象中包含了指针,叫做scan,表示需要gc scan;第二组没有指针,叫做noscan。提高gc scan性能。

    mcache初始没有span,G先从central动态申请span,并缓存在cache。

    central

    go1.10src untimemcentral.go

    type mcentral struct {
       lock      mutex
       spanclass spanClass
       nonempty  mSpanList // list of spans with a free object, ie a nonempty free list
       empty     mSpanList // list of spans with no free objects (or cached in an mcache)
    
       // nmalloc is the cumulative count of objects allocated from
       // this mcentral, assuming all spans in mcaches are
       // fully-allocated. Written atomically, read under STW.
       nmalloc uint64
    }
    
    • lock: 多个G并发从central申请span,所以需要lock,保证一致性
    • spanclass : 每个mcentral管理着一组有相同size class的span列表
    • nonempty: 指还有内存可用的span列表
    • empty: 指没有内存可用的span列表
    • nmalloc: 指累计分配的对象个数

    线程从central获取span步骤如下:

    1. 加锁
    2. 从nonempty列表获取一个可用span,并将其从链表中删除
    3. 将取出的span放入empty链表
    4. 将span返回给线程
    5. 解锁
    6. 线程将该span缓存进cache

    线程将span归还步骤如下:

    1. 加锁
    2. 将span从empty列表删除
    3. 将span加入nonempty列表
    4. 解锁

    heap

    central只管理特定的size class span,所以必然有一个更上层的数据结构,管理所有的sizeclass central,这就是heap。

    go1.10src untimemheap.go

    type mheap struct {
       lock      mutex
       
       spans []*mspan
    
       // Malloc stats.
       largealloc  uint64                  // bytes allocated for large objects
       nlargealloc uint64                  // number of large object allocations
       largefree   uint64                  // bytes freed for large objects (>maxsmallsize)
       nlargefree  uint64                  // number of frees for large objects (>maxsmallsize)
        
       // range of addresses we might see in the heap
       bitmap        uintptr // Points to one byte past the end of the bitmap
       bitmap_mapped uintptr
    
       arena_start uintptr
       arena_used  uintptr // Set with setArenaUsed.
    
       arena_alloc uintptr
       arena_end   uintptr
    
       arena_reserved bool
    
       central [numSpanClasses]struct {
          mcentral mcentral
          pad      [sys.CacheLineSize - unsafe.Sizeof(mcentral{})%sys.CacheLineSize]byte
       }
    }
    
    • spans:映射span -> page
    • large:大对象,>32K
    • bitmap: gc
    • arena: arena区相关信息,pages,堆区
    • central:通过size class管理span,每种size class对应两个central
     
    heap
  • 相关阅读:
    wex5 实战 框架拓展之2 事件派发与data刷新
    wex5 实战 框架拓展之1 公共data组件(Data)
    wex5 实战 HeidiSQL 导入Excel数据
    wex5 实战 手指触屏插件 hammer的集成与优劣
    wex5 实战 登陆帐号更换与用户id一致性
    wex5 实战 用户点评与提交设计技巧
    wex5 实战 省市县三级联动与地址薄同步
    wex5 实战 wex5与js的组件关系与执行顺序(父子与先后)
    wex5 实战 单页模式下的多页面数据同步
    [BZOJ]4237: 稻草人
  • 原文地址:https://www.cnblogs.com/saryli/p/10104789.html
Copyright © 2011-2022 走看看