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
  • 相关阅读:
    Asp.net Json 解析 与 直接用ip访问返回josn
    mysql 时间函数
    Android之TelephonyManager类的使用案例
    android:screenOrientation的说明 固定屏幕显示方向
    在android.app.Application中定义全局变量
    Android开发实例详解之IMF(Android SDK Sample—SoftKeyboard)
    Android InputMethodManager输入法简介
    android ImageView的属性android:scaleType,即ImageView.setScaleType(ImageView.ScaleType)
    VS编译常见错误枚举01
    Visual C++ Samples-------------Code Project
  • 原文地址:https://www.cnblogs.com/saryli/p/10104789.html
Copyright © 2011-2022 走看看