当前位置: 首页 > news >正文

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,用来缓存小块内存分配,减少锁竞争。
    • mcachemheap分配,存储小块内存(≤32KB)。
  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的垃圾回收器使用三色标记清除算法,主要分以下几个阶段:

  1. 标记阶段:
    • 从根对象(全局变量、栈变量、寄存器变量)开始,标记所有可达对象。
  2. 清除阶段:
    • 将未标记的对象回收,释放到mcachemheap
   func gcSweep() {// 遍历所有span,清理未使用的对象for _, s := range mheap_.spans {s.sweep()}}

4. 内存释放

源码位置: runtime/malloc.go

Go会主动将不再使用的大块内存返还给操作系统,调用sysUnusedsysFree实现。

关键点:

  • 小对象:
    • 释放到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 内存管理中核心模块 mcachemheap 的代码实现,可以更好地理解它们的协同工作方式。以下是详细的源码分析:


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 会被重用以分配同类大小的对象。
  • tinytinyoffset:
    • 用于小对象分配(如 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
}

工作流程:

  1. 根据 size 计算 size class
  2. 查找对应的 span:
    • 如果 span 有空闲块,从 freeindex 取一个。
    • 如果 span 已满,从 mheap 中分配新的 span。
  3. 返回分配的对象地址。

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 扩展
}

工作流程:

  1. freelist 中取出一个空闲的 span。
  2. 如果 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)
}

工作流程:

  1. 通过 spanclass 确定 span 类型。
  2. 重置 span 的元数据。
  3. 将 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 的协作流程

  1. 分配内存:
    • 小对象: 先从 mcache 中分配。
    • 大对象: 直接通过 mheap 分配。
  2. 回收内存:
    • mcache 释放的内存会回收到 mheap,进入 freelist
  3. GC 的作用:
    • 清理未使用的对象。
    • 调用 mcache.releaseAllmheap.freeSpan 释放无用的 span。

4. 小结一下

  • mcache 是每个 P (逻辑处理器) 的本地缓存,优化小对象分配的性能。
  • mheap 是全局堆管理器,负责大对象分配和全局内存回收。
  • 两者通过 span 的共享和回收机制协作,兼顾性能与内存利用率。
  • 垃圾回收器(GC)在这个体系中扮演清理者的角色,保证内存的高效使用。

关注威哥爱编程,成长路上一起努力,点个赞再走呗。

相关文章:

Go语言内存分配源码分析学习笔记

大家好&#xff0c;我是V 哥。GO GO GO&#xff0c;今天来说一说Go语言内存分配问题&#xff0c;Go语言内存分配的源码主要集中在runtime包中&#xff0c;它实现了Go语言的内存管理&#xff0c;包括初始化、分配、回收和释放等。下面来对这些过程详细分析一下&#xff0c;先赞后…...

【jvm】方法区常用参数有哪些

目录 1. -XX:PermSize2. -XX:MaxPermSize3. -XX:MetaspaceSize&#xff08;Java 8及以后&#xff09;4. -XX:MaxMetaspaceSize&#xff08;Java 8及以后&#xff09;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&#xff0c;下载后&#xff0c;直接双击运行&#xff0c;我这里默认安装到…...

LLM文档对话 —— pdf解析关键问题

一、为什么需要进行pdf解析&#xff1f; 最近在探索ChatPDF和ChatDoc等方案的思路&#xff0c;也就是用LLM实现文档助手。在此记录一些难题和解决方案&#xff0c;首先讲解主要思想&#xff0c;其次以问题回答的形式展开。 二、为什么需要对pdf进行解析&#xff1f; 当利用L…...

MySQL单表查询时索引使用情况

本文针对 MySQL 单表查询时索引使用的几种场景情况进行分析。 假设有一个表如下&#xff1a; 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" 指出了一个问题&#xff0c;即在非主线程中尝试启用或禁用套接字通知器&#xff08;QSocketNotifier&#xff09;…...

入门到精通mysql数据(四)

5、运维篇 5.1、日志 5.1.1、错误日志 错误日志是MySQL中最重要的日志之一,它记录了当mysqld启动和停止,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。 该日志是默认开启的,默认存放目录/var/log…...

Java 设计模式 详解

在Java开发中&#xff0c;设计模式是一种常见的、成熟的解决方案&#xff0c;用于应对特定的设计问题和复杂性管理。以下是一些常用的设计模式&#xff0c;它们可以分为三类&#xff1a;创建型模式、结构型模式和行为型模式。 一、创建型模式 创建型模式主要负责对象的创建&a…...

卡尔曼滤波学习资料汇总

卡尔曼滤波学习资料汇总 其实&#xff0c;当初的目的&#xff0c;是为了写 MPU6050 的代码的&#xff0c;然后不知不觉学了那么多&#xff0c;也是因为好奇、感兴趣吧 有些还没看完&#xff0c;之后笔记也会同步更新的 学习原始材料 【卡尔曼滤波器】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机器人。 具体配置步骤&#xff1a;打开西门子博图配置软件&#xff0c;添加PLC。这是配置的第一步&am…...

自动化运维-检测Linux服务器CPU、内存、负载、IO读写、机房带宽和服务器类型等信息脚本

前言&#xff1a;以上脚本为今年8月1号发布的&#xff0c;当时是没有任何问题&#xff0c;但现在脚本里网络速度测试py文件获取不了了&#xff0c;测速这块功能目前无法实现&#xff0c;后面我会抽时间来研究&#xff0c;大家如果有建议也可以分享下。 脚本内容&#xff1a; #…...

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的用户时&#xff0c;才会执行&#xff0c;如果…...

从视频帧生成点云数据、使用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:…...

工化企业内部能源能耗过大 落实能源管理

一、精准监测与数据分析 实时准确的数据采集 企业能耗管理系统能够对企业内各种能源&#xff08;如电、水、气、热等&#xff09;的使用情况进行实时监测。通过安装在能源供应线路和设备上的智能传感器&#xff0c;可以精确地采集能源消耗的各项数据&#xff0c;包括瞬时流量、…...

LSTM 和 LSTMCell

1. LSTM 和 LSTMCell 的简介 LSTM (Long Short-Term Memory): 一种特殊的 RNN&#xff08;循环神经网络&#xff09;&#xff0c;用于解决普通 RNN 中 梯度消失 或 梯度爆炸 的问题。能够捕获 长期依赖关系&#xff0c;适合处理序列数据&#xff08;如自然语言、时间序列等&…...

python成长技能之正则表达式

文章目录 一、认识正则表达式二、使用正则表达式匹配单一字符三、正则表达式之重复出现数量匹配四、使用正则表达式匹配字符集五、正则表达式之边界匹配六、正则表达式之组七、正则表达式之贪婪与非贪婪 一、认识正则表达式 什么是正则表达式 正则表达式&#xff08;英语&…...

解决docker报Error response from daemon Get httpsregistry-1.docker.iov2错误

解决docker报Error response from daemon: Get "https://registry-1.docker.io/v2/"错误 报错详情 首先先看一下问题报错效果,我想要拉去nacos-serve&#xff1a;1.1.4的镜像&#xff0c;报如下错误&#xff0c;从报错信息可以看到&#xff0c;用于网络的愿意&…...

【论文分享】利用多源大数据衡量街道步行环境的老年友好性:以中国上海为例

本次给大家带来一篇SCI论文的全文翻译&#xff01;该论文考虑了绿化程度、可步行性、安全性、形象性、封闭性和复杂性这六个指标&#xff0c;提出了一种基于多源地理空间大数据的新型定量评价模型&#xff0c;用于从老年人和专家的角度评估街道步行环境的老年友好程度&#xff…...

说说软件工程中的“协程”

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

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

省略号和可变参数模板

本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...

【堆垛策略】设计方法

堆垛策略的设计是积木堆叠系统的核心&#xff0c;直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法&#xff0c;涵盖基础规则、优化算法和容错机制&#xff1a; 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则&#xff1a; 大尺寸/重量积木在下&#xf…...

CSS 工具对比:UnoCSS vs Tailwind CSS,谁是你的菜?

在现代前端开发中&#xff0c;Utility-First (功能优先) CSS 框架已经成为主流。其中&#xff0c;Tailwind CSS 无疑是市场的领导者和标杆。然而&#xff0c;一个名为 UnoCSS 的新星正以其惊人的性能和极致的灵活性迅速崛起。 这篇文章将深入探讨这两款工具的核心理念、技术差…...