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
相关文章
|
6天前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
53 1
|
2月前
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
238 1
|
2月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
278 0
|
2月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
193 0
|
2月前
|
Cloud Native Java 中间件
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
160 0
|
2月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
243 0
|
2月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
2月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
3月前
|
JSON 前端开发 Go
Go语言实战:创建一个简单的 HTTP 服务器
本篇是《Go语言101实战》系列之一,讲解如何使用Go构建基础HTTP服务器。涵盖Go语言并发优势、HTTP服务搭建、路由处理、日志记录及测试方法,助你掌握高性能Web服务开发核心技能。
|
3月前
|
Go
如何在Go语言的HTTP请求中设置使用代理服务器
当使用特定的代理时,在某些情况下可能需要认证信息,认证信息可以在代理URL中提供,格式通常是:
290 0