go内存分配器

阅读 意随行笔记draveness关于go的内存管理的部分总结

  1. 流程
    用户程序(mutator)通过分配器(allocator)从堆(heap)中获得新内存空间,通过收集器(collector)回收空间

  2. 分配器
    go采用空闲链表分配器,分配内存,并且采用隔离适应的方法

  3. 空闲链表分配器
    当用户程序申请内存时,空闲链表分配器会依次遍历空闲的内存块,找到足够大的内存,然后申请新的资源并修改链表

    意思是说,分配的内存空间必然是连续的,若这块不够,就看下一块够不够,直到找到足够大的一块,而不能是多块凑起来组合的(简而言之就是要连续内存)

  4. 隔离适应
    将内存分割成多个链表,每个链表中的内存块大小相同,申请内存时先找到满足条件的链表,再从链表中选择合适的内存块

  5. 分级

    1. 线程缓存(thread cache)
    2. 中心缓存(central cache)
    3. 页堆(page heap)
      线程缓存只属于当前线程,不需要加锁所以不存在竞争
      32k以上的内存申请需要从页堆里分配
  6. 内存管理的基本单位(mspan)
    mspan实现了go内存管理的隔离适应效果
    每个mspan能分配出去的对象大小(sizeclasses)是固定的
    每个mspan里都包含了指向上一个和下一个mspan的指针的字段,且指向的是同一个mcache下的同sizeclasses的内容

  7. 线程缓存(mcache)

    type mcache {
     ...
     alloc [numSpanClasses]*mspan //numSpanClasses = 67*2  = 134
     ...
    }

    mcache是绑定在GMP的P(调度器)上的
    alloc一开始为空的大小为134(67个指针类型的和67个非指针类型的)的数组,使用过程中再去对应规格的中心缓存(mcentral)动态申请mspan

  8. 中心缓存(mcentral)

    type mcentral struct {
     lock      mutex
      spanclass spanClass   //spanClass Id
      nonempty  mSpanList // 所有未被使用的空闲span
      empty     mSpanList // 已经被mcache拿走,未归还的会挂载到这里
    
      nmalloc uint64 //这个mcentral分配mspan的累积计数
    }
    1. 中心缓存是公共区域,多个mcache都可以去申请空间所以得加锁
    2. 每个中心缓存只管理一种大小(spanclass)的mspan(所以mcentral的总数不超过67 * 2 = 134)
    3. mcache来申请mspan时,先在nonempty里面看有没有能用的,没有再去empty里面找,都没有就去页堆里面重新再申请一些空间
  9. 页堆(mheap)
    每个heapArena大小为8B可以管理64M的内存空间
    最多有4M个heapArena
    所以最多能用256TB
    每个指针占8B
    所以元信息的大小为4M*8B=32M(也就是要用32M的内存去记录这256T的分配情况)


思考

  1. 单个大对象的大小是否不能超过单个heapArena能管理的大小(64M)

    否,可以多页储存一个大对象

  2. 针对大对象归属的mspan,里面管理的对象大小是不是可能不一样???(参考第六点,是否正确?)

    超过32KB大小的由特殊的class表示,该class ID为0,每个class只包含一个对象,所以也满足第六点

  3. 一个pages,mspan的个数是不是也只有134个?

  4. mspan里面的元素可能来源于不同的mheap?

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!