Go语言内存分配源码分析学习笔记
大家好,我是V 哥。GO GO GO,今天来说一说Go语言内存分配问题,Go语言内存分配的源码主要集中在runtime
包中,它实现了Go语言的内存管理,包括初始化、分配、回收和释放等。下面来对这些过程详细分析一下,先赞后看,绝不摆烂:
1. 内存管理初始化
源码位置: runtime/malloc.go
关键点:
mheap
初始化:mheap
是整个Go运行时的核心内存分配结构,用于管理大块内存。- 初始化时,Go会从操作系统中获取一大块内存作为堆空间,通过
sysAlloc
分配给mheap
。
func mallocinit() {mheap_.init() // 初始化全局mheap_}
mcache
初始化:- 每个P(逻辑处理器)有一个
mcache
,用来缓存小块内存分配,减少锁竞争。 mcache
由mheap
分配,存储小块内存(≤32KB)。
- 每个P(逻辑处理器)有一个
func allocmcache() *mcache {c := new(mcache)c.refill() // 预填充小内存块return c}
2. 内存分配
Go的内存分配分为以下几种场景:
2.1 小对象分配(≤32KB)
- 使用
mcache
中的内存。 mcache
按大小类(class)分配,这些类通过sizeclasses
数组定义。- 分配时,调用
mcache.alloc
,如果mcache中没有可用的内存块,会从mheap
中拉取。
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {if size <= maxSmallSize {// 小对象分配c := getmcache() // 获取当前P的mcaches := c.alloc(size, needzero)return s}}
2.2 大对象分配(>32KB)
- 直接从
mheap
中分配大块内存。 - 使用
span
(连续内存块)管理这些大块内存。
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {if size > maxSmallSize {// 大对象分配s := mheap_.allocSpan(size)return s}}
3. 垃圾回收(GC)
源码位置: runtime/mgc.go
Go的垃圾回收器使用三色标记清除算法,主要分以下几个阶段:
- 标记阶段:
- 从根对象(全局变量、栈变量、寄存器变量)开始,标记所有可达对象。
- 清除阶段:
- 将未标记的对象回收,释放到
mcache
或mheap
。
- 将未标记的对象回收,释放到
func gcSweep() {// 遍历所有span,清理未使用的对象for _, s := range mheap_.spans {s.sweep()}}
4. 内存释放
源码位置: runtime/malloc.go
Go会主动将不再使用的大块内存返还给操作系统,调用sysUnused
或sysFree
实现。
关键点:
- 小对象:
- 释放到
mcache
。 - 如果
mcache
满了,释放到mheap
。
- 释放到
- 大对象:
- 直接释放到
mheap
。 - 如果
mheap
中内存长时间未使用,释放给操作系统。
- 直接释放到
5. 内存分配中的优化机制
5.1 线程本地缓存(mcache)
- 减少全局锁竞争。
- 小对象分配从
mcache
中直接获取。
5.2 内存对齐
- Go保证分配的内存地址按对象大小对齐(如8字节、16字节等),以提高访问效率。
5.3 分配池(Free List)
- 回收的内存会进入Free List,供后续快速分配。
5.4 GC触发条件
- 当堆的增长超过特定比例(默认100%)时触发GC。
Go的内存分配机制结合了现代内存分配的多种优化技术,能够高效地处理并发场景。关键点在于:
- 小对象通过
mcache
优化分配速度。 - 大对象通过
mheap
管理,提高内存利用率。 - 垃圾回收器负责自动清理无用内存,保证程序健壮性。
- 同时,内存释放机制及时将多余内存返还给操作系统,避免浪费。
6. mcache 和 mheap
深入分析 Go 内存管理中核心模块 mcache
和 mheap
的代码实现,可以更好地理解它们的协同工作方式。以下是详细的源码分析:
1. mcache 模块
1.1 mcache 数据结构
mcache
是每个 P (逻辑处理器) 的本地内存缓存,目的是减少对全局堆的锁争用。
它的源码定义在 runtime/mcache.go
。
type mcache struct {alloc [numSpanClasses]*mspan // 每个 size class 分配一个 spantiny uintptr // 小对象分配缓存tinyoffset uintptr // tiny 的当前偏移量local_nlookup uintptr // 本地分配次数...
}
字段解释:
alloc
:- 存储分配的 spans,按
size class
分类。 - 每个类的 span 会被重用以分配同类大小的对象。
- 存储分配的 spans,按
tiny
和tinyoffset
:- 用于小对象分配(如
mallocgc
)。
- 用于小对象分配(如
local_nlookup
:- 用于统计本地内存分配的次数。
1.2 mcache 的主要方法
1.2.1 分配内存 (mcache.alloc
)
当分配小对象时,调用 alloc
方法从 mcache
中获取内存:
func (c *mcache) alloc(size uintptr, needzero bool) unsafe.Pointer {sc := sizeToClass(size) // 根据 size 找到对应的 size classs := c.alloc[sc]if s == nil || s.freeindex == s.nelems { // 当前缓存中没有可用的 span,从 mheap 中获取s = mheap_.allocSpan(sc)if s == nil {throw("out of memory")}c.alloc[sc] = s}...return obj
}
工作流程:
- 根据
size
计算size class
。 - 查找对应的
span
:- 如果
span
有空闲块,从freeindex
取一个。 - 如果
span
已满,从mheap
中分配新的 span。
- 如果
- 返回分配的对象地址。
1.2.2 释放内存 (mcache.releaseAll
)
当 GC 发生时,mcache
会将所有未使用的 spans 返还给 mheap
。
func (c *mcache) releaseAll() {for i := range c.alloc {s := c.alloc[i]if s != nil {mheap_.freeSpan(s) // 释放到 mheapc.alloc[i] = nil}}
}
2. mheap 模块
2.1 mheap 数据结构
mheap
是全局的堆管理器,负责分配和回收大块内存(span),以及为 mcache
提供支持。它的源码定义在 runtime/mheap.go
。
type mheap struct {spans []*mspan // 全局管理的 spansfreelist [numSpanClasses]*mspan // 每个 size class 的空闲列表arenas [maxArenas]*heapArena // 内存分配的区域lock mutex // 全局锁...
}
字段解释:
spans
:- 按页索引管理的所有 spans。
freelist
:- 每个
size class
的空闲 span 链表。
- 每个
arenas
:- 堆内存分配的底层区域,映射到操作系统的物理内存。
lock
:- 对全局堆操作加锁,避免并发问题。
2.2 mheap 的主要方法
2.2.1 分配 span (mheap.allocSpan
)
当 mcache
需要新的 span 时,会调用 mheap.allocSpan
:
func (h *mheap) allocSpan(sc spanClass) *mspan {lock(&h.lock) // 加锁,防止并发冲突s := h.freelist[sc]if s != nil {h.freelist[sc] = s.next // 从 freelist 获取 spanunlock(&h.lock)return s}...unlock(&h.lock)return h.grow(sc) // freelist 没有时,从 arenas 扩展
}
工作流程:
- 从
freelist
中取出一个空闲的 span。 - 如果
freelist
为空,调用grow
方法,从arenas
分配新的 span。
2.2.2 回收 span (mheap.freeSpan
)
当 mcache
或垃圾回收器释放内存时,调用 mheap.freeSpan
:
func (h *mheap) freeSpan(s *mspan) {lock(&h.lock) // 加锁sc := s.spanclass()s.reset() // 重置 span 状态s.next = h.freelist[sc]h.freelist[sc] = s // 回收到 freelistunlock(&h.lock)
}
工作流程:
- 通过
spanclass
确定 span 类型。 - 重置 span 的元数据。
- 将 span 加入
freelist
链表。
2.3 mheap 内存增长 (mheap.grow
)
当 freelist
无法满足分配请求时,从底层 arenas
分配新的 span:
func (h *mheap) grow(sc spanClass) *mspan {p := sysAlloc(_PageSize * npage, &memstats.heap_sys) // 从操作系统分配物理内存if p == nil {throw("out of memory")}s := newMSpan() // 创建新的 spans.init(p, npage) ...return s
}
3. mcache 与 mheap 的协作流程
- 分配内存:
- 小对象: 先从
mcache
中分配。 - 大对象: 直接通过
mheap
分配。
- 小对象: 先从
- 回收内存:
mcache
释放的内存会回收到mheap
,进入freelist
。
- GC 的作用:
- 清理未使用的对象。
- 调用
mcache.releaseAll
和mheap.freeSpan
释放无用的 span。
4. 小结一下
mcache
是每个 P (逻辑处理器) 的本地缓存,优化小对象分配的性能。mheap
是全局堆管理器,负责大对象分配和全局内存回收。- 两者通过
span
的共享和回收机制协作,兼顾性能与内存利用率。 - 垃圾回收器(GC)在这个体系中扮演清理者的角色,保证内存的高效使用。
关注威哥爱编程,成长路上一起努力,点个赞再走呗。
相关文章:
Go语言内存分配源码分析学习笔记
大家好,我是V 哥。GO GO GO,今天来说一说Go语言内存分配问题,Go语言内存分配的源码主要集中在runtime包中,它实现了Go语言的内存管理,包括初始化、分配、回收和释放等。下面来对这些过程详细分析一下,先赞后…...
【jvm】方法区常用参数有哪些
目录 1. -XX:PermSize2. -XX:MaxPermSize3. -XX:MetaspaceSize(Java 8及以后)4. -XX:MaxMetaspaceSize(Java 8及以后)5. -Xnoclassgc6. -XX:TraceClassLoading7.-XX:TraceClassUnLoading 1. -XX:PermSize 1.设置JVM初始分配的永久…...

JAVA环境的配置
首先找到JDK环境的官网。 Java Archive Downloads - Java SE 8u211 and laterhttps://www.oracle.com/java/technologies/javase/javase8u211-later-archive-downloads.html 我下载的最后一个x64.exe,下载后,直接双击运行,我这里默认安装到…...

LLM文档对话 —— pdf解析关键问题
一、为什么需要进行pdf解析? 最近在探索ChatPDF和ChatDoc等方案的思路,也就是用LLM实现文档助手。在此记录一些难题和解决方案,首先讲解主要思想,其次以问题回答的形式展开。 二、为什么需要对pdf进行解析? 当利用L…...
MySQL单表查询时索引使用情况
本文针对 MySQL 单表查询时索引使用的几种场景情况进行分析。 假设有一个表如下: CREATE TABLE single_table (id INT NOT NULL AUTO_INCREMENT,key1 VARCHAR(100),key2 INT,key3 VARCHAR(100),key_part1 VARCHAR(100),key_part2 VARCHAR(100),key_part3 VARCHAR(1…...

Qt邮箱程序改良版(信号和槽)
上一版代码可以正常使用,但是会报错 上一篇文章 错误信息 "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread" 指出了一个问题,即在非主线程中尝试启用或禁用套接字通知器(QSocketNotifier)…...

入门到精通mysql数据(四)
5、运维篇 5.1、日志 5.1.1、错误日志 错误日志是MySQL中最重要的日志之一,它记录了当mysqld启动和停止,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。 该日志是默认开启的,默认存放目录/var/log…...
Java 设计模式 详解
在Java开发中,设计模式是一种常见的、成熟的解决方案,用于应对特定的设计问题和复杂性管理。以下是一些常用的设计模式,它们可以分为三类:创建型模式、结构型模式和行为型模式。 一、创建型模式 创建型模式主要负责对象的创建&a…...

卡尔曼滤波学习资料汇总
卡尔曼滤波学习资料汇总 其实,当初的目的,是为了写 MPU6050 的代码的,然后不知不觉学了那么多,也是因为好奇、感兴趣吧 有些还没看完,之后笔记也会同步更新的 学习原始材料 【卡尔曼滤波器】1_递归算法_Recursive P…...

linux003.在ubuntu中安装cmake的方法
1.cmake安装程序下载 https://cmake.org/files/v3.30/ 2.解压并下载包 解压cmake压缩包 tar -xvzf cmake.tar.gz进入解压目录 cd cmake-<version>编辑~/.bashrc nano ~/.bashrc在文件的末尾添加如下代码 export PATH/home/xwl/software/cmake/bin:$PATH然后运行以…...

EtherNet/IP转Profinet网关连接发那科机器人配置实例解析
本案例主要展示了如何通过Ethernet/IP转Profinet网关实现西门子1200PLC与发那科搬运机器人的连接。所需的设备有西门子1200PLC、开疆智能Ethernet/IP转Profinet网关以及Fanuc机器人。 具体配置步骤:打开西门子博图配置软件,添加PLC。这是配置的第一步&am…...

自动化运维-检测Linux服务器CPU、内存、负载、IO读写、机房带宽和服务器类型等信息脚本
前言:以上脚本为今年8月1号发布的,当时是没有任何问题,但现在脚本里网络速度测试py文件获取不了了,测速这块功能目前无法实现,后面我会抽时间来研究,大家如果有建议也可以分享下。 脚本内容: #…...
ubuntu24.04设置开机自启动Eureka
ubuntu24.04设置开机自启动Eureka 之前我们是在/root/.bashrc的文件中增加了一条命令 nohup java -jar /usr/software/eurekaServer-auth-prd-03.jar > /usr/software/log.log 2>&1 &但上面这条命令只有在登录root的用户时,才会执行,如果…...

从视频帧生成点云数据、使用PointNet++模型提取特征,并将特征保存下来的完整实现。
文件地址 https://github.com/yanx27/Pointnet_Pointnet2_pytorch?spm5176.28103460.0.0.21a95d27ollfze Pointnet_Pointnet2_pytorch\log\classification\pointnet2_ssg_wo_normals文件夹改名为Pointnet_Pointnet2_pytorch\log\classification\pointnet2_cls_ssg "E:…...
工化企业内部能源能耗过大 落实能源管理
一、精准监测与数据分析 实时准确的数据采集 企业能耗管理系统能够对企业内各种能源(如电、水、气、热等)的使用情况进行实时监测。通过安装在能源供应线路和设备上的智能传感器,可以精确地采集能源消耗的各项数据,包括瞬时流量、…...
LSTM 和 LSTMCell
1. LSTM 和 LSTMCell 的简介 LSTM (Long Short-Term Memory): 一种特殊的 RNN(循环神经网络),用于解决普通 RNN 中 梯度消失 或 梯度爆炸 的问题。能够捕获 长期依赖关系,适合处理序列数据(如自然语言、时间序列等&…...

python成长技能之正则表达式
文章目录 一、认识正则表达式二、使用正则表达式匹配单一字符三、正则表达式之重复出现数量匹配四、使用正则表达式匹配字符集五、正则表达式之边界匹配六、正则表达式之组七、正则表达式之贪婪与非贪婪 一、认识正则表达式 什么是正则表达式 正则表达式(英语&…...
解决docker报Error response from daemon Get httpsregistry-1.docker.iov2错误
解决docker报Error response from daemon: Get "https://registry-1.docker.io/v2/"错误 报错详情 首先先看一下问题报错效果,我想要拉去nacos-serve:1.1.4的镜像,报如下错误,从报错信息可以看到,用于网络的愿意&…...

【论文分享】利用多源大数据衡量街道步行环境的老年友好性:以中国上海为例
本次给大家带来一篇SCI论文的全文翻译!该论文考虑了绿化程度、可步行性、安全性、形象性、封闭性和复杂性这六个指标,提出了一种基于多源地理空间大数据的新型定量评价模型,用于从老年人和专家的角度评估街道步行环境的老年友好程度ÿ…...

说说软件工程中的“协程”
在软件工程中,协程(coroutine)是一种程序运行的方式,可以理解成“协作的线程”或“协作的函数”。以下是对协程的详细解释: 一、协程的基本概念 定义:协程是一组序列化的子过程,用户能像指挥家…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...