【go】内存分配模型

2024-09-07 23:36
文章标签 go 内存 分配 模型

本文主要是介绍【go】内存分配模型,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

内存是怎么分配给对象的?
内存分配优化的地方是?
讲讲golang内存分配模型?

ans:
1.按照对象的大小分配:先算出对象的大小如果是tiny对象,就从tiny block中获取地址和偏移量,将对象打包到mcache;如果是16B以上32k以内就先从mcache获得对应class级别的span;如果mcache没有就从mcenter中获取,如果mcenter没有就创建一个mspan从free树或scav树中获得内存页。如果堆中没有就系统调用申请内存,后再扫描树。
2.优化的地方是使用了类似于tcmalloc模型,向每个p预分配2k的缓存减少系统分配的次数且mcahe是p私有的不用加锁操作;
3.将page组织进span作为内存管理的基本单位;mcache中根据page的页数范围不同class的span链表;mcenter作为缓存池存放不同class的两个span链表,一个链表叫empty不确定里面是否有空闲的对象空间,另一个是noempty所有span都至少有1个空闲的对象空间.mheap存放两个二查排序空闲已回收树,free树:空闲未垃圾回收。scav树:空闲已垃圾回收。

面对三连问,你是不是和我一样懵逼了?以下是我查询资料学习总结的知识体系,一起看下去吧🙀

1.存储的基本知识

计算机存储体系:
[图片]

cpu的运算速度很快,而其他的硬件速度慢,为了好好利用cpu的性能所以加入了高速缓存和层层缓存层。
引入虚拟缓存后,操作系统向进程屏蔽了物理物理,cpu查询内存如果高速cache没有命中就会查找页表从磁盘将物理内存加载进缓存更新页表映射。由于引入虚拟内存所以物理内存的并发访问问题的级别由进程级转为了多线程级别。

2.TCMalloc

2.1基础

学习内存先得了解两个内存区域:
栈内存:栈中的内存会自动回收,内存管理简单,分配快。
堆内存:堆上的内存需要手动分配和释放,浪费gc。
堆内存管理分为三个重要步骤:
分配内存、(使用内存)、回收内存、组织内存块
一个内存块中包含了:元数据、用户数据、对齐字段
分配:从堆中把内存分配出来,将信息存进内存块,将内存块组织为链表。
释放:从链表中的内存块取出标记为未使用。
分配顺序:从链表中获得未使用的内存块没有再从内存中分配。

2.2tcmalloc机制

引入TCmalloc
同一进程所有线程共享空间,为了更快的分配内存,为每个线程预分配一些内存,再申请小内存可以直接从缓存分配。因为只有一次系统调用且分配无需加锁,因此,可以达到快速分配的目的。
[图片]

内存管理的基本单位是page,一组连续的page被称为span,tcCache是线程级别的缓存,包含有不同级别的span链表,centralCache是所有线程共享的缓存包含不同级别的span只是访问需要加锁,pageHeap是堆内存的抽象保存若干span链表和large span set保存中大对象.
小对象的分配流程:ThreadCache -> CentralCache -> HeapPage,大部分时候,ThreadCache缓存都是足够的,不需要去访问CentralCache和HeapPage,无锁分配加无系统调用,分配效率是非常高的。
中对象分配流程:直接在PageHeap中选择适当的大小即可,128 Page的Span所保存的最大内存就是1MB。
大对象分配流程:从large span set选择合适数量的页面组成span,用来存储数据。

3.Go的内存管理与分配

[图片]

不同级别的内存管理对象:
page:8kb
span:内存管理的基本单位
mcahe:线程级别的缓存,无锁访问,mcache与p绑定
mcentral:线程共享的缓存,加锁访问,每个级别的span有有指针的链表和无指针的链表
mheap:从操作系统中分配的内存页按照span组织起来,组织形式是二叉排序树:
free树:空闲并未垃圾回收
scav:空闲已经垃圾回收
[图片]

小对象是在mcache中分配的,而大对象是直接从mheap分配的,从小对象的内存分配看起。
不同大小的对象内存分配:
小对象:

  1. 计算对象所需内存size
  2. Size class和是否需要指针获得span class
  3. 获取对应class级别的cspan
    a.mcache ->b. mcenteal ->c.mheap
    span内的所有内存块都被占用时,没有剩余空间继续分配对象,mcache会向mcentral申请1个span,mcache拿到span后继续分配对象。
    mcentral有两个链表。
  • nonempty:这个链表里的span,所有span都至少有1个空闲的对象空间。这些span是mcache释放span时加入到该链表的。
  • empty:这个链表里的span,所有的span都不确定里面是否有空闲的对象空间。当一个span交给mcache的时候,就会加入到empty链表。
    mcentral会先从nonempty搜索满足条件的span,如果没有找到再从emtpy搜索满足条件的span,然后把找到的span交给mcache。
    mcentral需要向mheap提供需要的内存页数和span class级别,然后它优先从free中搜索可用的span,如果没有找到,会从scav中搜索可用的span,如果还没有找到,它会向OS申请内存,再重新搜索2棵树,必然能找到span。如果找到的span比需求的span大,则把span进行分割成2个span,其中1个刚好是需求大小,把剩下的span再加入到free中去,然后设置需求span的基本信息,然后交给mcentral。
    大对象:
    99%的流程与mcentral向mheap申请内存的相同
    栈内存:每个goroutine都有自己的栈,栈的初始大小是2KB,100万的goroutine会占用2G,但goroutine的栈会在2KB不够用时自动扩容,当扩容为4KB的时候,百万goroutine会占用4GB。在rpc调用(grpc invoke)时,栈会发生扩容(runtime.morestack),也就意味着在读写routine内的任何rpc调用都会导致栈扩容, 占用的内存空间会扩大为原来的两倍,4kB的栈会变为8kB,100w的连接的内存占用会从8G扩大为16G(全双工,不考虑其他开销)
  1. 根据分配对象的大小,选用不同的结构做分配。包括 3 种情况:
    1. 小于 16B 的用 mcache 中的 tiny 分配器分配;
    2. 大于 32KB 的对象直接使用堆区分配;
    3. 16B 和 32KB 之间的对象用 mspan 分配。
  2. 现在我们假定分配对象大小在 16B 和 32KB 之间。在 mcache 中找到合适的 mspan 结构,如果找到了就直接用它给对象分配内存。
  3. 我们这里假定此时没有在 mcache 中找到合适的 mspan。需要到 mcentral 结构中查找到一个 mspan 结构并返回。虽然 mcentral 结构对 mspan 的大小和是否空闲进行了分类管理,但是它对所有的 P 都是共享的,所以每个 P 访问 mcentral 结构都要加锁。
  4. 假定 Go 运行时在进行了一些扫描回收操作之后,在 mcentral 结构还是没有找到合适的 mspan。Go 运行时就会建立一个新的 mspan,并找到 heapArea 分配相应的页面,把页面地址和数量写入 mspan 中。然后,把 mspan 插入 mcentral 结构中,返回的同时将 mspan 插入 mcache 中。最后用这个新的 mspan 分配对象,返回对象地址。
    微小对象分配:
    从tiny block中分配,这些对象被打包到线程本地缓存(mcache)中,在同一个内存块中存储多个 tiny 对象,从而减少碎片化和全局锁的争用。

这篇关于【go】内存分配模型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1146487

相关文章

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

Go路由注册方法详解

《Go路由注册方法详解》Go语言中,http.NewServeMux()和http.HandleFunc()是两种不同的路由注册方式,前者创建独立的ServeMux实例,适合模块化和分层路由,灵活性高... 目录Go路由注册方法1. 路由注册的方式2. 路由器的独立性3. 灵活性4. 启动服务器的方式5.

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

Spring AI Alibaba接入大模型时的依赖问题小结

《SpringAIAlibaba接入大模型时的依赖问题小结》文章介绍了如何在pom.xml文件中配置SpringAIAlibaba依赖,并提供了一个示例pom.xml文件,同时,建议将Maven仓... 目录(一)pom.XML文件:(二)application.yml配置文件(一)pom.xml文件:首

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

Go Mongox轻松实现MongoDB的时间字段自动填充

《GoMongox轻松实现MongoDB的时间字段自动填充》这篇文章主要为大家详细介绍了Go语言如何使用mongox库,在插入和更新数据时自动填充时间字段,从而提升开发效率并减少重复代码,需要的可以... 目录前言时间字段填充规则Mongox 的安装使用 Mongox 进行插入操作使用 Mongox 进行更

如何在本地部署 DeepSeek Janus Pro 文生图大模型

《如何在本地部署DeepSeekJanusPro文生图大模型》DeepSeekJanusPro模型在本地成功部署,支持图片理解和文生图功能,通过Gradio界面进行交互,展示了其强大的多模态处... 目录什么是 Janus Pro1. 安装 conda2. 创建 python 虚拟环境3. 克隆 janus

本地私有化部署DeepSeek模型的详细教程

《本地私有化部署DeepSeek模型的详细教程》DeepSeek模型是一种强大的语言模型,本地私有化部署可以让用户在自己的环境中安全、高效地使用该模型,避免数据传输到外部带来的安全风险,同时也能根据自... 目录一、引言二、环境准备(一)硬件要求(二)软件要求(三)创建虚拟环境三、安装依赖库四、获取 Dee

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允