• Golang 内存分配


    一般的内存分配

    一般而言, 内存都会被程序分为以下几个逻辑块

    • 全局区: 存放全局变量
    • 栈区: 存放函数中的基础类类型变量
    • 堆区: 动态分配的内存, 比如 go 的切片
    • 常量区: 存放常量数据
    • 程序代码区: 存放程序本身的代码

    go 内存分配思想

    go 内置了运行时的编程语言(runtime), 所谓运行时, 就是在程序开始时就申请了一大块的虚拟内存, 由 go 自己进行分配和管理. 用来避免在运行的时候再向操作系统申请内存, 带来性能问题.

    go 的内存分配核心思想是:

    • 每次从操作系统申请一大块内存, 由 go 来对内存进行分配和管理, 减少系统调用
    • 内存分配算法采用 google 的 TCMalloc 算法, 把内存切分的很细, 再通过多级进行管理, 降低锁粒度
    • 回收对象内存时, 并不是真正的将内存返回给操作系统, 而是放回自己的大块内存中等待复用, 只有闲置过多时才会尝试返回部分内存给操作系统.

    go 的内存结构

    go 的内存结构分为三大块:

    • spans(512MB): 存放 span 指针, 每个指针指向 arena 的一个 page
    • bitmap(16GB): 保存 arena 对应的某个地址是否存在对象, 和对象是否 GC 信息
    • arena(512GB): 真正存放值的地方, area 按照8KB 分配若干个 page

    arena

    arena 存放了真正的值, 很多人也把他称之为heap, heap 从两个角度来看, 有两种说法

    • 从内存分配角度: 按照 8KB 分配若干个 page, page合在一起组成了 heap
    • 从使用角度: heap 存放若干个对象

    spans

    spans 可以理解为 heap 的户口信息, 用来管理 heap 区域的数据, spans 存放的是mspan指针, 每个 page都属于某个mspan

    bitmap

    bitmap存放了 arena 中的对象标记, 比如标记对应的地址中是否存在对象, 是否被 GC 标记

    因此, bitmap 主要还是为了服务 GC

    go 内存管理组件

    go 内存管理组件有以下几个:

    • mspan: 内存管理基本单元, 管理
    • mcache: 缓存, 每个运行时的 goroutine 都会绑定一个 mcache, mcache 会分配这个 goroutine 运行时需要的内存空间(mspan)
    • mcentral: 为所有 mcache 切分好后备的 mspan, 收集给定大小和登记的所有 span
    • mheap代表Go程序持有的所有堆空间。还会管理闲置的span,需要时向操作系统申请新内存

    mspan

    上面提到了名词mspan, mspan 可以说是 go 内存管理的基本单元, 而 page 是内存存储的基本单元

    mspan 可以解决内存碎片问题, 我们知道, 内存的地址都是连到一起的, 连续的, 举个例子, 同一时间申请了两个内存, a 和 b, a 和 b 是连续的, 两者在内存中是挨着的, 此时 b 被清理了, b 的地址内数据变成空, 此时申请存放新的数据, 并不会检测到 b 是空了就直接放到 b, 而是会在 b 后面申请 c, 这就造成了在内存中很可能有很多空位, 这就是内存碎片

    go 为了解决内存碎片问题. 将内存分为67种, 每种有不同数量的 page, 这每一种就是 mspan.每次分配时, 根据数据的不同, 分配给不同的 mspan. 当某个 mspan 被清理后, 在语言内部将这个标记为已清理, 等待下一次重新使用

    mchahe

    为了避免多线程申请内存时不断的加锁, goroutine为每个线程分配了 span 内存块的缓存, 这个缓存即是 mcache, 每个goroutine都会绑定的一个 mcache, 避免了各个goroutine申请内存时存在锁竞争的情况

    在 GMP 并发调度模型中, 每个 G 都绑定一个 mcache, 因此不会出现大家一起占用一个内存, 导致需要加锁竞争的情况, 提高速度

    mcentral

    mcentral保存一种特定类型的全局 mspan 列表,包括已分配出去的和未分配出去的, 目的是为所有 mcache 提供切分好的 mspan

    有67种 mspan, 也就有67种 mcentral

    每个 mcentral 都会包含两个 mspan的列表:

    • 没有空闲对象或 mspan已经被 mcache缓存的 mspan列表
    • 有空闲对象的 mspan列表

    由于 mspan是全局的,会被所有的 mcache访问,所以会出现并发性问题,因而 mcentral会存在一个锁。

    mheap

    mheap可以认为是Go程序持有的整个堆空间, mheap全局唯一,可以认为是个全局变量

    mheap 包含了整个 go 内部的 heap 数据信息, 除了 mcache, mcache被包含在GMP 中的 G 里

    因为有些对象通过 mheap 分配(大于32kb), 而 mheap 全局唯一, 而 mcache有时候需要进行申请内存分配, 因为 GMP 中 G 不止一个, 所以为了防止资源竞争, 在 mheap 中有一个互斥锁, 保证同一时刻只有一个 G 可以申请内存

  • 相关阅读:
    由少林寺比武想到软件行业分工
    微软SQL 报表服务的研究
    图形化窗体表单设计器
    多层数据源处理复杂数据结构
    Universal menu command handle pattern
    使用foreach来读取记录
    C#计算20902个汉字的拼音首字母
    软件的价值
    虚控件在GUI编程中的应用
    深刻的检讨书
  • 原文地址:https://www.cnblogs.com/chnmig/p/16744183.html
Copyright © 2020-2023  润新知