【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

相关文章

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

Golang的CSP模型简介(最新推荐)

《Golang的CSP模型简介(最新推荐)》Golang采用了CSP(CommunicatingSequentialProcesses,通信顺序进程)并发模型,通过goroutine和channe... 目录前言一、介绍1. 什么是 CSP 模型2. Goroutine3. Channel4. Channe

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

Go语言使用Buffer实现高性能处理字节和字符

《Go语言使用Buffer实现高性能处理字节和字符》在Go中,bytes.Buffer是一个非常高效的类型,用于处理字节数据的读写操作,本文将详细介绍一下如何使用Buffer实现高性能处理字节和... 目录1. bytes.Buffer 的基本用法1.1. 创建和初始化 Buffer1.2. 使用 Writ

Go Gorm 示例详解

《GoGorm示例详解》Gorm是一款高性能的GolangORM库,便于开发人员提高效率,本文介绍了Gorm的基本概念、数据库连接、基本操作(创建表、新增记录、查询记录、修改记录、删除记录)等,本... 目录1. 概念2. 数据库连接2.1 安装依赖2.2 连接数据库3. 数据库基本操作3.1 创建表(表关

关于Java内存访问重排序的研究

《关于Java内存访问重排序的研究》文章主要介绍了重排序现象及其在多线程编程中的影响,包括内存可见性问题和Java内存模型中对重排序的规则... 目录什么是重排序重排序图解重排序实验as-if-serial语义内存访问重排序与内存可见性内存访问重排序与Java内存模型重排序示意表内存屏障内存屏障示意表Int

如何测试计算机的内存是否存在问题? 判断电脑内存故障的多种方法

《如何测试计算机的内存是否存在问题?判断电脑内存故障的多种方法》内存是电脑中非常重要的组件之一,如果内存出现故障,可能会导致电脑出现各种问题,如蓝屏、死机、程序崩溃等,如何判断内存是否出现故障呢?下... 如果你的电脑是崩溃、冻结还是不稳定,那么它的内存可能有问题。要进行检查,你可以使用Windows 11

Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)

《Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)》:本文主要介绍Python基于火山引擎豆包大模型搭建QQ机器人详细的相关资料,包括开通模型、配置APIKEY鉴权和SD... 目录豆包大模型概述开通模型付费安装 SDK 环境配置 API KEY 鉴权Ark 模型接口Prompt

Go信号处理如何优雅地关闭你的应用

《Go信号处理如何优雅地关闭你的应用》Go中的优雅关闭机制使得在应用程序接收到终止信号时,能够进行平滑的资源清理,通过使用context来管理goroutine的生命周期,结合signal... 目录1. 什么是信号处理?2. 如何优雅地关闭 Go 应用?3. 代码实现3.1 基本的信号捕获和优雅关闭3.2

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行