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)是一种程序运行的方式,可以理解成“协作的线程”或“协作的函数”。以下是对协程的详细解释: 一、协程的基本概念 定义:协程是一组序列化的子过程,用户能像指挥家…...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
