面试官:谈谈 Go 内存分配策略

news2024/12/24 12:06:43

大家好,我是木川

Go语言内置运行时(就是runtime),抛弃了传统的内存分配方式,改为自主管理。这样可以自主地实现更好的内存使用模式,比如内存池、预分配等等。这样,不会每次内存分配都需要进行系统调用。

一、设计思想

  • 内存分配算法采用Google的TCMalloc算法,每个线程都会自行维护一个独立的内存池,进行内存分配时优先从该内存池中分配,当内存池不足时才会向加锁向全局内存池申请,减少系统调用并且避免不同线程对全局内存池的锁竞争

  • 把内存规格切分的比较细,分级管理,以降低锁的粒度

  • 回收对象内存时,并没有将其真正释放掉,只是放回预先分配的大块内存中,以便复用。只有内存闲置过多的时候,才会尝试归还部分内存给操作系统,降低整体开销

二、分配组件

Go的内存管理组件主要有:mspanmcachemcentralmheap

e47e9784410af736bfd9941012738b78.png

内存管理单元:mspan

mspan是 内存管理的基本单元,该结构体中包含 nextprev 两个字段,它们分别指向了前一个和后一个mspan,每个mspan 都管理 npages 个大小为 8KB 的 page,一个span 是由多个page组成的,这里的页不是操作系统中的内存页,它们是操作系统内存页的整数倍。page是内存存储的基本单元,“对象”放到page

type mspan struct {
 next *mspan // 后指针
 prev *mspan // 前指针
 startAddr uintptr // 管理页的起始地址,指向page
 npages    uintptr // 页数
 spanclass   spanClass // 规格
 ...
}

type spanClass uint8

Go 有 68 种不同大小的 spanClass,用于小对象的分配

const _NumSizeClasses = 68
var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536,1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768}
4cae535fdbc9a2ce5c2da035f31d924a.png

如果按照序号为1的spanClass(对象规格为8B)分配,每个span占用堆的字节数:8k,mspan可以保存1024个对象

如果按照序号为2的spanClass(对象规格为16B)分配,每个span占用堆的字节数:8k,mspan可以保存512个对象

如果按照序号为67的spanClass(对象规格为32K)分配,每个span占用堆的字节数:32k,mspan可以保存1个对象

字段含义:

  • class:class ID,每个span结构中都有一个class ID, 表示该span可处理的对象类型

  • bytes/obj:该class代表对象的字节数

  • bytes/span:每个span占用堆的字节数,也即页数*页大小

  • objects: 每个span可分配的对象个数,也即(bytes/spans)/(bytes/obj)

  • waste bytes: 每个span产生的内存碎片,也即(bytes/spans)%(bytes/obj)

大于32k的对象出现时,会直接从heap分配一个特殊的span,这个特殊的span的类型(class)是0,  只包含了一个大对象

线程缓存:mcache

mcache 管理线程在本地缓存的mspan,每个goroutine绑定的P都有一个mcache字段

type mcache struct {
    alloc [numSpanClasses]*mspan
}

_NumSizeClasses = 68
numSpanClasses = _NumSizeClasses << 1

mcacheSpan Classes作为索引管理多个用于分配的mspan,它包含所有规格的mspan。它是_NumSizeClasses的2倍,也就是68*2=136,其中*2是将spanClass分成了有指针和没有指针两种,方便与垃圾回收。对于每种规格,有2个mspan,一个mspan不包含指针,另一个mspan则包含指针。对于无指针对象的mspan在进行垃圾回收的时候无需进一步扫描它是否引用了其他活跃的对象。

mcache在初始化的时候是没有任何mspan资源的,在使用过程中会动态地从mcentral申请,之后会缓存下来。当对象小于等于32KB大小时,使用mcache的相应规格的mspan进行分配。

中心缓存:mcentral

mcentral管理全局的mspan供所有线程使用,全局mheap变量包含central字段,每个 mcentral 结构都维护在mheap结构内

type mcentral struct {
 spanclass spanClass // 指当前规格大小

 partial [2]spanSet // 有空闲object的mspan列表
 full    [2]spanSet // 没有空闲object的mspan列表
}

每个mcentral管理一种spanClass的mspan,并将有空闲空间和没有空闲空间的mspan分开管理。partial和 full的数据类型为spanSet,表示 mspans集,可以通过pop、push来获得mspans

type spanSet struct {
    spineLock mutex
    spine     unsafe.Pointer // 指向[]span的指针
    spineLen  uintptr        // Spine array length, accessed atomically
    spineCap  uintptr        // Spine array cap, accessed under lock

    index headTailIndex  // 前32位是头指针,后32位是尾指针
}

简单说下mcachemcentral获取和归还mspan的流程:

  • 获取;加锁,从partial链表找到一个可用的mspan;并将其从partial链表删除;将取出的mspan加入到full链表;将mspan返回给工作线程,解锁。

  • 归还;加锁,将mspanfull链表删除;将mspan加入到partial链表,解锁。

页堆:mheap

mheap管理Go的所有动态分配内存,可以认为是Go程序持有的整个堆空间,全局唯一

var mheap_ mheap
type mheap struct {
    lock      mutex    // 全局锁
    pages     pageAlloc // 页面分配的数据结构
    allspans []*mspan // 所有通过 mheap_ 申请的mspans
  // 堆
    arenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena
 
  // 所有中心缓存mcentral
    central [numSpanClasses]struct {
        mcentral mcentral
        pad      [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte
    }
    ...
}

所有mcentral的集合则是存放于mheap中的。mheap里的arena 区域是堆内存的抽象,运行时会将 8KB 看做一页,这些内存页中存储了所有在堆上初始化的对象。运行时使用二维的 runtime.heapArena 数组管理所有的内存,每个 runtime.heapArena 都会管理 64MB 的内存。

当申请内存时,依次经过 mcachemcentral 都没有可用合适规格的大小内存,这时候会向 mheap 申请一块内存。然后按指定规格划分为一些列表,并将其添加到相同规格大小的 mcentral非空闲列表 后面

三、分配流程

小对象 [0, 32KB]:依次尝试线程缓存、中心缓存、堆 分配内存

大对象 (32KB, +∞):直接尝试堆分配内存

发生内存逃逸的对象:直接尝试堆分配内存

具体介绍下小对象的分配流程:

  • 首先通过计算使用的大小规格

  • 然后使用线程缓存mcache中对应大小规格的块分配。

  • 如果中心缓存mcentral中没有可用的块,则向堆mheap申请,并根据算法找到最合适的mspan

  • 如果申请到的mspan 超出申请大小,将会根据需求进行切分,以返回用户所需的页数。剩余的页构成一个新的 mspan 放回 mheap 的空闲列表。

  • 如果 mheap 中没有可用 span,则向操作系统申请一系列新的页(最小 1MB)

c58e7d2a8cd6b763598a47b24af911d1.png

最后给自己的原创 Go 面试小册打个广告,如果你从事 Go 相关开发,欢迎扫码购买,目前 10 元买断,加下面的微信发送支付截图额外赠送一份自己录制的 Go 面试题讲解视频

c92a406bc83f413722714a22b53a2069.jpeg

3fa7f5fe7d3fe5c10c7d159da5398313.png

如果对你有帮助,帮我点一下在看或转发,欢迎关注我的公众号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1083344.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

找不到x3daudio1_7.dll怎么解决?x3daudio1_7.dll的5个修复方法

电脑已经成为我们生活和工作中不可或缺的一部分。然而&#xff0c;在使用电脑的过程中&#xff0c;我们常常会遇到各种问题&#xff0c;其中之一就是“找不到x3daudio1_7.dll&#xff0c;无法运行启动软件或者游戏”。这个问题可能会影响到我们的正常使用&#xff0c;甚至可能导…

2023 年 Arm A-Profile 架构发展

随着人工智能 (AI) 的兴起和安全威胁的加剧,计算需求不断发展,作为世界设备核心的基础计算架构也必须不断发展。这就是为什么我们的工程团队向普遍存在的 Arm 架构添加新功能和技术,然后软件团队确保软件尽可能无缝地适应这些未来的功能和技术。 Arm架构是如何开发的 Arm …

一种高速1553B总线通信接口模块

一种高速1553B总线通信接口模块 引言 -- 在现代航空电子系统中&#xff0c;总线通信技术已成为不可或缺的一部分1553B总线作为军用标准总线&#xff0c;被广泛应用于各种军事和航天领域。本文主要介绍了一种高速1553B总线通信接口模块的设计与实现方法。该模块不仅具有高可…

Unity基础课程之物理引擎5-射线的使用方法总结

在实际游戏开发时&#xff0c;不可避免地要用到各种射线检测。即便是一个不怎么用到物理系统的游戏&#xff0c;也很可能要用到射线检测机制。换句话说&#xff0c;射线检测在现代游戏开发中应用得非常广泛&#xff0c;超越了物理游戏的范围。下面简单举几个例子。 &#xff0…

游戏设计模式专栏(八):Cocos中最常见的设计模式之一

点击上方亿元程序员关注和★星标 引言 大家好&#xff0c;我是亿元程序员&#xff0c;一位有着8年游戏行业经验的主程。 本系列是《和8年游戏主程一起学习设计模式》&#xff0c;让糟糕的代码在潜移默化中升华&#xff0c;欢迎大家关注分享收藏订阅。 组合模式是一种在Cocos…

前端JavaScript入门到精通,javascript核心进阶ES6语法、API、js高级等基础知识和实战 —— JS进阶(三)

思维导图 1.编程思想 1.1 面向过程编程 1.2 面向对象编程 (oop) 2. 构造函数 3. 原型 3.1 原型 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IE…

精品Python的农村振兴平台防贫助农

《[含文档PPT源码等]精品Python的农村振兴平台设计与实现-爬虫》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程等&#xff01; 软件开发环境及开发工具&#xff1a; 开发语言&#xff1a;python 使用框架&#xff1a;Django 前端技术&#xff1a;J…

LeetCode【279】完全平方数

题目&#xff1a; 思路&#xff1a; https://www.acwing.com/solution/leetcode/content/114877/ 1、给定数字&#xff0c;是由若干个完全平方数组合而成&#xff0c;求使用的最小的完全平方数的个数&#xff0c;如果这些完全平方数已知&#xff0c;则完全等同于百元百鸡问题…

adobe firefly image2重磅发布

萤火虫图像2&#xff08;Firefly Image2&#xff09;是由adobe的一种新的图像生成模型。它是萤火虫图像的改进版本&#xff0c;具有以下特点&#xff1a; 更逼真的图像&#xff1a;萤火虫图像2使用了更先进的图像生成技术&#xff0c;能够生成更逼真的图像。更丰富的细节&…

codesys【手轮】

一般4线&#xff0c;也有6线 电压&#xff1a;DC5v&#xff0c;12v&#xff0c;24v 脉冲当量&#xff1a;一圈100脉&#xff0c;25脉 计数器不能【-1000】【1000】 因为一循环会多一个计数 要【-1000】【999】或者【-999】【1000】 PLC计数案例&#xff1a; // QQ750273008…

AndroidStudio模拟器,没有Google Play的就有ROOT权限

正确选择版本 测试 D:\>adb shell emulator64_x86_64:/ $ su emulator64_x86_64:/ #

selinux相关学习笔记-简单selinux部分的解决

selinux问题判断&#xff1a; 1 日志查看&#xff1a; logcat -b all 查看所有日志 如果自己程序有类似如下的avc:denied打印&#xff0c;基本上可以认为有selinux问题&#xff0c;这里有avc: denied相关的关键字 I Thread-2: type1400 audit(0.0:53): avc: denied { search }…

1688拍立淘API接口分享

拍立淘接口&#xff0c;顾名思义&#xff0c;就是通过图片搜索到相关商品列表。通过此接口&#xff0c;可以实现图片搜索爆款商品等功能。 接口地址&#xff1a;1688.item_search_img 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&…

C++11新特性(右值引用,万能转发)

这篇文章是C的重中之重&#xff0c;通过这篇文章你能体会到C/C大佬们对性能的极致追求&#xff0c;你能感受到独属C/C人的浪漫&#xff0c;对高消耗的零容忍&#xff0c;对高性能的不倦探索。右值引用是由Scott Meyers在他的著名书籍《Effective C》中提出的&#xff0c;因为其…

【软件测试学习】—软件测试知识点总结(二)

【软件测试学习】—软件测试的分类&#xff08;二&#xff09; 一、软件测试的分类 二、软件的生命周期 三、软件测试的工作流程 四、软件测试用例设计方法 &#xff08;一&#xff09;、等价类划分 定义&#xff1a;等价类划分是一种典型的、重要的黑盒测试的方法&#xff…

Python:如何在一个月内学会爬取大规模数据

Python爬虫为什么受欢迎 如果你仔细观察&#xff0c;就不难发现&#xff0c;懂爬虫、学习爬虫的人越来越多&#xff0c;一方面&#xff0c;互联网可以获取的数据越来越多&#xff0c;另一方面&#xff0c;像 Python这样的编程语言提供越来越多的优秀工具&#xff0c;让爬虫变得…

Python+Tkinter 图形化界面基础篇:多线程和异步编程

PythonTkinter 图形化界面基础篇&#xff1a;多线程和异步编程 引言为什么需要多线程和异步编程&#xff1f;使用多线程多线程示例步骤 1 &#xff1a;导入必要的模块步骤 2 &#xff1a;创建主窗口和按钮步骤 3 &#xff1a;创建下载线程步骤 4 &#xff1a;启动主事件循环 使…

SIT1050,可替代TIJA050,5V 供电,±40V 接口耐压,1Mbps 高速 CAN 总线收发器

SIT1050 是一款应用于 CAN 协议控制器和物理总线之间的接口芯片&#xff0c;可应用于卡车、公交、 小汽车、工业控制等领域&#xff0c;速率可达到 1Mbps &#xff0c;具有在总线与 CAN 协议控制器之间进行差分信 号传输的能力。 特点 ➢ 完全兼容 “ ISO 11898 ” 标…

谷粒商城中消息队列的使用

目录 一、概述 二、步骤 三、说明 四、详细步骤 五、总结 一、概述 在订单服务中使用到了消息队列 具体就是解决关单还有自动解锁库存的功能 其实就是使用消息队列的延迟队列的功能 达到一个定时任务的作用 使用消息队列到达最终一致性的效果 比如说库存 当下单之后 …

超强大的 Nginx 可视化管理平台 Nginx-Proxy-Manager

一、简介 Nginx-Proxy-Manager 是一个基于 Web 的 Nginx 服务器管理工具&#xff0c;它允许用户通过浏览器界面轻松地管理和监控 Nginx 服务器。通过 Nginx-Proxy-Manager&#xff0c;可以获得受信任的 SSL 证书&#xff0c;并通过单独的配置、自定义和入侵保护来管理多个代理…