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)在这个体系中扮演清理者的角色,保证内存的高效使用。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/60143.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android ART知多少?

Android 虚拟机 ART&#xff08;Android Runtime&#xff09;是 Android 平台上的应用程序运行时环境&#xff0c;用于执行应用程序的字节码。ART 自 Android 5.0&#xff08;Lollipop&#xff09;开始取代了 Dalvik&#xff0c;成为 Android 的默认运行时环境。本文将从以下几…

C++ —— 剑斩旧我 破茧成蝶—C++11

江河入海&#xff0c;知识涌动&#xff0c;这是我参与江海计划的第2篇。 目录 1. C11的发展历史 2. 列表初始化 2.1 C98传统的{} 2.2 C11中的{} 2.3 C11中的std::initializer_list 3. 右值引用和移动语义 3.1 左值和右值 3.2 左值引用和右值引用 3.3 引用延长生命周期…

使用ufw配置防火墙,允许特定范围IP访问

在ubuntu上使用 ufw&#xff08;Uncomplicated Firewall&#xff09;允许特定 IP 地址或子网访问某个端口&#xff08;如 22 端口&#xff09;非常简单。以下是具体的步骤&#xff1a; 1. 安装 UFW&#xff08;如果尚未安装&#xff09; 首先&#xff0c;确保 ufw 已经安装。…

推荐15个2024最新精选wordpress模板

以下是推荐的15个2024年最新精选WordPress模板&#xff0c;轻量级且SEO优化良好&#xff0c;适合需要高性能网站的用户。中文wordpress模板适合搭建企业官网使用。英文wordpress模板&#xff0c;适合B2C网站搭建&#xff0c;功能强大且兼容性好&#xff0c;是许多专业外贸网站的…

(计算机毕设)基于SpringBoot+Vue的房屋租赁系统的设计与实现

博主可接毕设设计&#xff01;&#xff01;&#xff01; 各种毕业设计源码只要是你有的题目我这里都有源码 摘 要 社会的发展和科学技术的进步&#xff0c;互联网技术越来越受欢迎。网络计算机的生活方式逐渐受到广大人民群众的喜爱&#xff0c;也逐渐进入了每个用户的使用。互…

python蓝桥杯刷题2

1.最短路 题解&#xff1a;这个采用暴力枚举&#xff0c;自己数一下就好了 2.门牌制作 题解&#xff1a;门牌号从1到2020&#xff0c;使用for循环遍历一遍&#xff0c;因为range函数无法调用最后一个数字&#xff0c;所以设置成1到2021即可&#xff0c;然后每一次for循环&…

华为ID机试 -- 分糖果 E100

题目描述 小明从糖果盒中随意抓一把糖果&#xff0c;每次小明会取出一半的糖果分给同学们。 当糖果不能 平均分配只时&#xff0c;小明可以选择从糖果盒中(假设盒中糖果足够)取出一个糖果或放回一个糖果。 小明最少需要多少次(取出、放回和平均分配均记一次)&#xff0c;能将…

react-amap海量点优化

前言&#xff1a;高版本的react-amap 支持MassMarkers 组件用于一次性添加大量的标记点。本次优化的海量点是在低版本react-amap的基础上。官方推荐使用聚合useCluster属性来优化海量点的渲染。 直接附上代码&#xff1a; import React, { Component } from "react"…

深度学习中的Pixel Shuffle和Pixel Unshuffle:图像超分辨率的秘密武器

在深度学习的计算机视觉任务中&#xff0c;提升图像分辨率和压缩特征图是重要需求。Pixel Shuffle和Pixel Unshuffle是在超分辨率、图像生成等任务中常用的操作&#xff0c;能够通过转换空间维度和通道维度来优化图像特征表示。本篇文章将深入介绍这两种操作的原理&#xff0c;…

Unity类银河战士恶魔城学习总结(P132 Merge skill tree with skill Manager 把技能树和冲刺技能相组合)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了解锁技能后才可以使用技能&#xff0c;先完成了冲刺技能的锁定解锁 Dash_Skill.cs using System.Collections; using System…

正则表达式完全指南,总结全面通俗易懂

目录 元字符 连接符 限定符 定位符 修饰符&#xff08;标记&#xff09; 运算符优先级 普通字符集及其替换 零宽断言 正向先行断言 负向先行断言 正向后发断言 负向后发断言 捕获组 普通捕获组 命名捕获组 PS:非捕获组 正则表达式在线测试: 正则在线测试工具 …

qt之QFTP对文件夹(含嵌套文件夹和文件)、文件删除下载功能

一、前言 主要功能如下&#xff1a; 1.实现文件夹的下载和删除&#xff0c;网上很多资料都是单独对某个路径的文件操作的&#xff0c;并不能对文件夹操作 2.实现目标机中含中文名称自动转码&#xff0c;有些系统编码方式不同&#xff0c;下载出来的文件会乱码 3.实现ftp功能…

代码随想录算法训练营第五十一天|Day51 图论

岛屿数量 深搜 https://www.programmercarl.com/kamacoder/0099.%E5%B2%9B%E5%B1%BF%E7%9A%84%E6%95%B0%E9%87%8F%E6%B7%B1%E6%90%9C.html 思路 #include <stdio.h> #define MAX_SIZE 50 int grid[MAX_SIZE][MAX_SIZE]; int visited[MAX_SIZE][MAX_SIZE]; int N, M; …

HCIP --OSI七层参考模型回顾、TCP/UDP协议复习

目录 一、OSI 二、重要的三个协议报头格式 名词注解 MTU 封装 解封装 PDU ARP DNS TCP/IP与OSI的区别 三、数据包转发过程 四、获取目标ip地址方式 五、获取目标mac地址方式 六、交换机的工作原理 七、TCP/UDP TCP&#xff08;Transmission Control Protocol&a…

ORIN NX 16G安装中文输入法

刷机版本为jetpack5.14.刷机之后预装了cuda、cudnn、opencv、tensorrt等&#xff0c;但是发现没有中文输入&#xff0c;所以记录一下安装流程。 jetson NX是arm64架构的&#xff0c;sougoupinyin只支持adm架构的&#xff0c;所以要选择安装Google pinyin 首先打开终端&#x…

解决背景图因为图片路径中携带括号导致图片无法显示的问题

本人是在uniapp中写微信小程序遇到了这个问题&#xff0c;调用后端接口传来的数据&#xff0c;发现有些背景图能正常显示&#xff0c;但有些不能&#xff0c;最后发现问题是因为路径地址携带了()的原因 如下&#xff1a; 注&#xff1a;该地址无法正常访问&#xff0c;放在这只…

Node == Sequelize 使用文档

目录 1. 简介 2. 安装 3. 连接到数据库 4. 测试连接 5. 关闭连接 6. 模型&#xff08;Model&#xff09; define.sync({ force: true }); belongsTo paranoid 7. 增删改查&#xff08;CRUD&#xff09; 7.1 查询&#xff08;Query&#xff09; 7.2 插入&#xff08;…

2411rust,1.75.0

原文 Rust团队很高兴地声明推出Rust的新版本1.75.0. 如果你rustup安装了以前版本的Rust,你可如下取1.75.0: $ rustup update stable1.75.0稳定版中的功能 async fn和特征中的返回位置impl Trait. 指针字节偏移API 原始指针(*const T和*mutT)过去主要支持,T为单位的操作.如…

物联网智能技术的深入探讨与案例分析

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

Keil基于ARM Compiler 5的工程迁移为ARM Compiler 6的工程

环境&#xff1a; keil版本为5.38&#xff0c;版本务必高于5.30 STM32F4的pack包版本要高于2.9 软件包下载地址&#xff1a;https://zhuanlan.zhihu.com/p/262507061 一、更改Keil中编译器 更改后编译&#xff0c;会报很多错&#xff0c;先不管。 二、更改头文件依赖 观察…