Go 语言,内存的分配原理

简介: Go 中实现的内存分配器,简单的说就是维护了一大块全局内存,每个线程(Go 中的 P)维护一小块的私有内存,当私有内存不足时再向全局申请。内存分配与 GC(垃圾回收)有密切关系。

Go 中实现的内存分配器,简单的说就是维护了一大块全局内存,每个线程(Go 中的 P)维护一小块的私有内存,当私有内存不足时再向全局申请。内存分配与 GC(垃圾回收)有密切关系。

概念

为了方便自主管理内存,做法便是先向系统申请一块内存,然后将内存切割成小块,通过一定的内存分配算法管理内存。以64位系统为例,Golang 程序启动时会向系统申请的内存如下图所示:

image.png

预申请的内存划分为 spans、bitmap、arena 三部分

arena 即所谓的堆区,应用中需要的内存从这里分配,spans 和 bitmaps 是用来管理 arena 的。

  • arena :大小为 512 G,为了方便管理把arena区域划分成一个个的page,每个 page 为 8KB,一共有 512GB/8KB 个页;
  • spans :区域存放 span 的指针,每个指针对应一个 page,所以 span区域的大小为(512GB/8KB)*指针大小8byte = 512M
  • bitmap :区域大小也是通过 arena 计算出来,不过主要用于GC。

span

span是用于管理 arena 页的关键数据结构,每个 span 中包含1个或多个连续页,为了满足小对象分配,span中的一页会划分更小的粒度,而对于大对象比如超过页大小,则通过多页实现。

内存分配过程

针对待分配对象的大小不同有不同的分配逻辑:

  1. 申请一块较大的虚拟内存空间,用于内存分配及管理
  • 当空间不足时,向系统申请一块较大的内存,如100KB或者1MB
  • 申请到的内存块按特定的size,被分割成多种小块内存(go:_NumSizeClasses = 67),并用链表管理。创建对象时,按照对象大小,从空闲链表中查找到最适合的内存块。
  1. 销毁对象时,将对应的内存块返还空闲链表中以复用。
  2. 空闲内存达到最大值时,返还操作系统。

管理组件

go将内存分为三个层级,协程私有内存,全局内存,整体内存叶管理,也因此有这三个层级的内存管理工具。

image.png

  • mspan: mspan 并不直接拥有内存空间,它负责管理起始地址为 startAddr、级别(预分配页的个数)为 sizeclass 的连续地址空间。
  • mcache: Per-P私有cache,用于实现无锁的 object 分配,每个 mcache 有大小为67的 mspan 数组,存储不同级别大小的 mspan。
  • mcentral: 全局内存,为各个 cache 提供按大小划分好的 mspan,mcentral有个关键方法 cacheSpan(),它是整个分配的核心算法
  • mheap 是真实拥有虚拟地址的结构,page 管理,内存不足时向系统申请。

总结

  1. Golang 程序启动时申请一大块内存,并划分成spans、bitmap、arena 区域
  2. aren a区域按页划分成一个个小块
  3. span 管理一个或多个页
  4. mcentral 管理多个 span 供线程申请使用
  5. mcache 作为线程私有资源,资源来源于 mcentral
相关文章
|
24天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
50 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
8天前
|
Java 编译器 测试技术
go语言避免不必要的内存分配
【10月更文挑战第18天】
19 1
|
9天前
|
存储 算法 Java
Go语言的内存管理机制
【10月更文挑战第25天】Go语言的内存管理机制
15 2
|
25天前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
46 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
3月前
|
存储 安全 编译器
Go 内存分布
该文章深入分析了Go语言中值的内存分布方式,特别是那些分布在多个内存块上的类型,如切片、映射、通道、函数、接口和字符串,并讨论了这些类型的内部结构和赋值时的行为,同时指出了“引用类型”这一术语在Go中的使用可能会引起的误解。
53 5
Go 内存分布
|
3月前
|
关系型数据库 MySQL
MySQl优化:使用 jemalloc 分配内存
MySQl优化:使用 jemalloc 分配内存
|
3月前
|
Rust 安全 程序员
揭秘Rust语言的内存安全秘籍:如何构建坚不可摧的系统级应用?
【8月更文挑战第31天】Rust语言凭借其独特内存安全机制在编程领域脱颖而出,通过所有权、借用与生命周期等概念,在保证高性能的同时避免了缓冲区溢出等常见错误。本文深入探讨Rust的内存安全机制,并通过示例代码展示如何利用这些机制构建高效且可靠的系统。尽管这些机制增加了学习难度,但为软件开发奠定了坚实基础,使Rust成为系统、嵌入式及网络编程的理想选择。随着社区的发展,Rust将在未来软件开发中扮演更重要角色。
81 0
|
3月前
|
存储 NoSQL Java
Tair的发展问题之Tair对于不同存储介质(如内存和磁盘)的线程分配是如何处理的
Tair的发展问题之Tair对于不同存储介质(如内存和磁盘)的线程分配是如何处理的
|
3月前
|
存储 人工智能 JSON
深入理解 go reflect - 反射基本原理
深入理解 go reflect - 反射基本原理
50 0
|
3月前
|
缓存 安全 测试技术
深入理解 go sync.Map - 基本原理
深入理解 go sync.Map - 基本原理
35 0