Golang 内存之mspan、mcache、mcentral 和 mheap 数据结构

本文最新版本请查看原文:https://blog.haohtml.com/archives/29385

Golang中的内存部件组成关系如下图所示

components of memory allocation

golang 内存分配组件

在学习golang 内存时,经常会涉及几个重要的数据结构,如果不熟悉它们的情况下,理解它们就显得格外的吃力,所以本篇主要对相关的几个内存组件做下数据结构的介绍。

在 Golang 中,mcachemspanmcentralmheap 是内存管理的四大组件,mcache 管理线程在本地缓存的 mspan,页 mcentral 管理着全局的 mspan 为所有 mcache 提供所有线程。

根据分配对象的大小,内部会使用不同的内存分配机制,详细参考函数 mallocgo()

  • <16b 会使用微小对象内存分配器,主要使用 mcache.tinyXXX 这类的字段
  • 16-32b 从P下面的 mcache 中分配
  • >32b 直接从 mheap 中分配

对于golang中的内存申请流程,大家应该都非常熟悉了,这里不再进行详细描述。


Golang内存组件关系

mcache

在GPM关系中,会在每个 P 下都有一个 mcache 字段,用来表示内存信息。

在 Go 1.2 版本前调度器使用的是 GM 模型,将mcache 放在了 M 里,但发现存在诸多问题,期中对于内存这一块存在着巨大的浪费。每个M 都持有 mcachestack alloc,但只有在 M 运行 Go 代码时才需要使用的内存(每个 mcache 可以高达2mb),当 M 在处于 syscall网络请求 的时候是不需要的,再加上 M 又是允许创建多个的,这就造成了很大的浪费。所以从go 1.3版本开始使用了GPM模型,这样在高并发状态下,每个G只有在运行的时候才会使用到内存,而每个 G 会绑定一个P,所以它们在运行只占用一份 mcache,对于 mcache 的数量就是P 的数量,同时并发访问时也不会产生锁。

对于 GM 模型除了上面提供到内存浪费的问题,还有其它问题,如单一全局锁sched.Lock、goroutine 传递问题和内存局部性等。

P中,一个 mcache 除了可以用来缓存小对象外,还包含一些本地分配统计信息。由于在每个P下面都存在一个 ·mcache· ,所以多个 goroutine 并发请求内存时是无锁的。

mcache

当申请一个 16b 大小的内存时,会优先从运行当前G所在的P里的mcache字段里找到相匹配的mspan 规格,此时最合适的是图中 mspan3 规格。

mcache是从非GC内存中分配的,所以任何一个堆指针都必须经过特殊处理。源码文件:https://github.com/golang/go/blob/go1.16.2/src/runtime/mcache.go

type mcache struct {
    // 下方成员会在每次访问malloc时都会被访,所以为了更加高效的缓存将按组其放在这里
    nextSample uintptr // trigger heap sample after allocating this many bytes
    scanAlloc  uintptr // bytes of scannable heap allocated

    // 小对象缓存,<16b。推荐阅读"Tiny allocator"注释文档
    tiny       uintptr
    tinyoffset uintptr
    tinyAllocs uintptr

    // 下方成员不会在每次 malloc 时被访问
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

武昌库里写JAVA

您的鼓励将是我前进的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值