深入浅出 Golang 内存管理

news2024/11/15 19:59:44

了解内存管理~

前言:

本节课主要介绍了内存管理知识与自动内存管理机制,并对目前 Go 内存管理过程中存在的问题提出了解决方案,同时结合了上次课程学习的《Go 语言性能优化》相关知识,提供可行性的优化建议 …

image.png


自动内存管理

Go 语言作为相对新型的语言,自动内存管理肯定是少不了的了,和 Java 一样,Go 语言也有垃圾回收机制,可以实现自动内存管理,下面让我们来了解一些自动内存管理的相关知识。

🎈自动内存管理的概念:

程序运行时的内存分配策略有很多种:单一连续分配、固定分区分配、动态分区分配,现代的程序一般采用动态分区分配,会根据程序在运行时的需求动态地分配内存(malloc()),将动态分配地内存称作动态内存

image.png

自动内存管理是指由程序语言地运行时系统管理动态内存。

  • 避免手动内存管理,专注于实现业务逻辑;
  • 保证内存使用的正确性和安全性。

自动内存管理也可以叫垃圾回收(GC),主要负责:

  • 为新对象分配空间;
  • 找到存活对象;
  • 回收死亡对象的内存空间。
GC 相关概念
  • Mutator:业务线程,分配新对象,修改对象指向关系;
  • Collector:GC 线程,找到存活对象,回收死亡对象的内存空间;
  • Serial GC:只有一个 Collector 的 GC 算法;
  • Parallel GC:支持多个 Collectors 同时回收的 GC 算法;
  • Concurrent GC:Mutator(s) 和 Collector(s) 可以同时执行的 GC 算法。

image.png

🎯评价 GC 算法的几大指标:

  • 安全性:不能回收存活的对象;
  • 吞吐率:用来衡量 GC 时间的占比,其值为 1 - GC 时间 / 程序执行总时
  • 暂停时间:GC 可能会导致暂停业务,一般希望暂停时间越短越好;
  • 内存开销:GC 元数据开销。

在学习 Go 内存管理之前,我们先来了解一下垃圾回收的几种实现方案~

追踪垃圾回收

追踪垃圾回收是 GC 策略的其中一种,属于间接式的回收策略,这种策略不会直接寻找垃圾本身,而是先去找到存活的对象,然后反过来判断其余的对象应该被回收掉。

image.png

✔追踪垃圾回收的步骤:

  1. 标记根对象:静态变量、全局变量、常量、线程栈等;
  2. 标记:从根对象出发,找到所有可达对象;
  3. 清理:回收掉所有不可达对象。
    • 将存活对象复制到另外的内存空间(Copying GC)
    • 将死亡对象的内存标记为“可分配”(Mark-sweep GC)
    • 移动并整理存活对象(Mark-compact GC)

(根据对象的生命周期,会使用不同的标记和清理策略)

分代 GC(Generational GC)

分代 GC 是基于分代假说的内存管理策略,是一种比较常见的策略。

弱分代假说认为绝大多数对象都是朝生夕灭的,生命很短;强分代假说认为活得越久也就是熬过越多次垃圾收集过程的对象就越难以消亡,这两个分代假说共同奠定了垃圾回收的设计原则。

image.png

分代 GC 策略会给每个对象一个年龄(经历过 GC 的次数),将对象分成老年代和年轻代,把不同年龄的对象放在堆内存(Heap)的不同区域,为其制定不同的 GC 策略,从而降低整体内存管理的开销。

  • 年轻代(Young generation)

    • 常规的对象分配
    • 由于存活对象很少,可以采用 copying collection 策略
    • GC 吞吐率很高
  • 老年代(Old generation)

    • 对象趋向于一直存活,反复复制开销较大
    • 可以采用 mark-sweep collection 策略
引用计数(ARC)

引用计数是另一种 GC 策略,在这种策略下,每个对象都有一个与之关联的引用数目,只有引用数大于 0 的对象才存活。

image.png

⭐引用计数的优点:

  • 内存管理的操作被平摊到程序的执行过程中;
  • 内存管理不需要了解 runtime 的实现细节。

👎引用计数的缺点:

  • 维护引用计数的开销较大:通过原子操作保证对引用计数操作的原子性和可见性;
  • 无法回收环形数据结构(循环引用) —— weak reference;
  • 额外的内存开销:每个对象都引入额外的内存空间存储引用数目;
  • 回收内存时依然可能引发暂停。

image.png

✨引用计数 vs. 其他策略:

时机性能延迟
ARC引用计数为 0 马上回收
other GC定时扫描清理

Go 内存分配

Go 的内存分配基于两个思想:分块 & 缓存。

分块

Go 会提前将内存分块,给对象分配内存时会找一个尺寸最接近的块分配。

🎯目标: 为对象在堆(heap)上分配内存。

✔内存分块:

  • 调用系统调用 mmap() 向 OS 申请一大块内存,例如 4MB;
  • 先将内存划分成大块,例如 8KB,称作 mspan;
    • noscan mspan:分配不包含指针的对象(GC 不需要扫描)
    • scan mspan:分配包含指针的对象(GC 需要扫描)
  • 再将大块继续划分成特定大小的小块,用于对象分配。

🍬对象分配: 根据对象的大小,选择最合适的块返回。

image.png

缓存(cache)

Go 的内存分配器为内存做了多级不同的缓存,从而加快整体的内存分配速度。

线程缓存分配(Thread-Caching Malloc,TCMalloc)是用于分配内存的机制,Go 语言的内存分配器就借鉴了 TCMalloc 的设计实现高速的内存分配,它的核心理念是使用多级缓存将对象根据大小分类,并按照类别实施不同的分配策略。

image.png

☕分配过程:

  • 分配内存是指给 goroutine(简称为 g) 上的一段代码分配内存,g 会先绑定在 p(Go 线程)上;
  • 每个 p 包含一个 mcache 用于快速分配,用于为绑定与 p 上的 g 分配对象;
  • mcache 管理一组 mspan;
  • 当 mcache 中的 mspan 分配完毕,向 mcentral 申请带有未分配块的 mspan;
  • 当 mspan 中没有分配的对象,mspan 会被缓存在 mcentral 中,而不是立刻释放并归还给 OS。

image.png


Go 语言中的 GC 策略

了解了这么多内存管理的相关知识和垃圾回收策略,下面来一起看看 Golang 中采用的垃圾回收策略。

Go 语言的垃圾回收总体采用的是经典的 mark-sweep 算法,Go 语言的版本不同,采用的 GC 策略可能是不一样的。Go 1.5 正在实现的垃圾回收器是 “非分代的、非移动的、并发的、三色的标记清除垃圾收集器”。

Go 1.5 使用的三色标记法可以看成 mark-sweep 的增强版,这种方法的 mark 操作是可以渐进执行的而不需每次都扫描整个内存空间,,可以减少 stop the world(STW)的时间;分代 GC 策略在上文已经提及,但是 Go 1.5 还暂时没有采用,Go 官方表示会在 1.6 版本的 GC 优化中考虑~

随着 Golang 的推陈出新,Go 的垃圾回收性能一直在提升,但目前距离 Java 语言的 JVM 使用的成熟的垃圾回收系统还有一定差距。


Go 内存管理优化

Go 内存分配机制中的问题

对象分配是非常高频的操作,每秒分配 GB 级别的内存,小对象占比又比较高,加上分配路径长,这就导致 Go 内存分配比较耗时。

  • 分配路径长:g->m->p->mcache->mspan->memory block->return pointer
  • pprof():对象分配的函数是最频繁调用的函数之一
字节的优化方案:Balanced GC

Balanced GC 是字节跳动官方推出的针对 Go SDK 里面对象分配做的优化方案。

  • 每个 g 都绑定一大块内存(1KB),称作 goroutine allocation buffer(GAB);
  • GAB 用于 noscan 类型的小对象分配:< 128 B
  • 使用三个指针维护 GAB:base,end,top;
  • Bump pointer(指针碰撞)风格对象分配:无须和其他分配请求互斥,分配动作简单高效。

image.png

// 一次对象分配:
if top + size <= end {
    addr := top
    top += size
    return addr
}

💭技术细节:

GAB 对于 Go 内存管理来说是一个大对象。

  • 本质:将多个小对象的分配合并成一次大对象的分配。
  • 问题:GAB 的对象分配方式会导致内存被延迟释放。
  • 方案:移动 GAB 中存活的对象(用 copying GC 的算法管理小对象)
    • 当 GAB 总大小超过一定阈值时,将 GAB 中存活的对象复制到另外分配的 GAB 中;
    • 原先的 GAB 可以释放,避免内存泄漏。

Balanced GC 作为语言层面的性能优化,开启以后可以减少 CPU 负荷,降低核心接口时延,获得不错的性能收益。

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

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

相关文章

spring-boot怎么扫描不在启动类所在包路径下的bean

前言&#xff1a; 项目中有多个模块&#xff0c;其中有些模块的包路径不在启动类的子路径下&#xff0c;此时我们怎么处理才能加载到这些类&#xff1b; 1 使用SpringBootApplication 中的scanBasePackages 属性; SpringBootApplication(scanBasePackages {"com.xxx.xx…

C++linux高并发服务器项目实践 day5

Clinux高并发服务器项目实践 day5程序和进程单道、多道程序设计时间片并行和并发进程控制块&#xff08;PCB&#xff09;进程状态转换进程的状态进程相关命令进程号和相关函数进程创建父子进程的关系GDB多进程调试程序和进程 程序是包含一系列信息的文件&#xff0c;这些信息描…

你知道怎么实现定时任务吗?

诸位读者都知道笔者写东西都是用到才写&#xff0c;笔者的学习足迹自从参加工作之后就是 非系统 学习了&#xff0c;公司里源代码只要有笔者不知道的技术细节&#xff0c;笔者就会仔细的研究清楚&#xff0c;笔者是不喜欢给自己留下问题的那种学习习惯。 为何要写 笔者最近负…

如何使用Thymeleaf给web项目中的网页渲染显示动态数据?

编译软件&#xff1a;IntelliJ IDEA 2019.2.4 x64 操作系统&#xff1a;win10 x64 位 家庭版 服务器软件&#xff1a;apache-tomcat-8.5.27 目录一. 什么是Thymeleaf&#xff1f;二. MVC2.1 为什么需要MVC&#xff1f;2.2 MVC是什么&#xff1f;2.3 MVC和三层架构之间的关系及工…

AI绘图体验:想象力无限,创作无穷!(文生图)

基础模型&#xff1a;3D二次元 PIXEL ART &#xff08;1&#xff09;16-bit pixel art, outside of caf on rainy day, light coming from windows, cinematic still(电影剧照), hdr (2) 16-bit pixel art, island in the clouds, by studio ghibli&#xff08;吉卜力工作室…

配置基于WSL2的Docker环境并支持CUDA

导言 Content 正如前文windows 10 开启WSL2介绍的&#xff0c;我们可以在windows10中使用linux子系统。今天本文介绍如何在此基础上安装Docker并支持在wsl中使用GPU。 准备工作 加入windows insider preview。建议选Dev通道&#xff0c;不要选Beta。 安装Nvidia WSL2-compa…

【数据结构】-计数排序

&#x1f387;作者&#xff1a;小树苗渴望变成参天大树 &#x1f389; 作者宣言&#xff1a;认真写好每一篇博客 &#x1f38a;作者gitee:link 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; 文章目录前言一、计数排序二、排序算法复杂度…

Nginx网站服务配置

一、Nginx概述 1.1 Nginx概述 Nginx&#xff1a; Nginx 是开源、高性能、高可靠的 Web 和反向代理服务器&#xff0c;而且支持热部署&#xff0c;几乎可以做到 7 * 24 小时不间断运行&#xff0c;即使运行几个月也不需要重新启动&#xff0c;还能在不间断服务的情况下对软件…

分布式计算技术(上):经典计算框架MapReduce、Spark 解析

当一个计算任务过于复杂不能被一台服务器独立完成的时候&#xff0c;我们就需要分布式计算。分布式计算技术将一个大型任务切分为多个更小的任务&#xff0c;用多台计算机通过网络组装起来后&#xff0c;将每个小任务交给一些服务器来独立完成&#xff0c;最终完成这个复杂的计…

07 -全局状态管理

全局状态管理 7-1&#xff1a;开篇 在上一章中我们完成了 “一半” 的文章搜索功能&#xff0c;并且留下了一些问题。那么这些历史残留的问题&#xff0c;我们将会在本章节中通过 全局状态管理工具 进行处理。 那么究竟什么是 全局状态管理工具&#xff0c;如何在 uniapp 中…

【Flutter进阶】聊一聊组件中的生命周期、状态管理及局部重绘

前言 说到生命周期&#xff0c;熟悉Android开发的小伙伴一定第一时间会想到Activity的生命周期&#xff0c;由于在Flutter中一切都是组件&#xff0c;所以组件的生命周期其实是类似的。 在这个过程中组件的状态——State就非常重要&#xff0c;它记录这整个组件内可变部分的状…

【SSM整合】1—Spring和Mybatis整合

⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记链接&#x1f449;https://github.com/A-BigTree/Code_Learning ⭐⭐⭐⭐⭐⭐ Spring专栏&#x1f449;https://blog.csdn.net/weixin_53580595/category_12279588.html SpringMVC专栏&#x1f449;htt…

linux安装kafka

目录 目录 一.安装包准备&#xff1a; 二.解压安装&#xff1a; 先将该安装包放入到/opt/install目录&#xff1a; 解压该文件到soft目录中&#xff1a; 改名&#xff0c;方便后续使用&#xff1a; 三修改其中配置和配置环境变量&#xff1a; 3.1 修改/opt/soft/kafka2…

camunda工作流引擎开发架构

Camunda的开发架构可以分为前端开发架构和后端开发架构。 前端开发架构&#xff1a; Camunda前端使用Angular框架进行开发&#xff0c;主要包括以下组件&#xff1a; 1、Cockpit&#xff1a;流程监控和管理界面。 2、Tasklist&#xff1a;任务管理和审批界面。 3、Admin&…

答题积分小程序云开发实战-开篇:项目介绍以及效果图

答题积分小程序云开发实战 开篇:项目介绍以及效果图 前言 我也看过不少的册子或者文章,大部分都很优秀,但也有的就长篇累牍,从时代背景讲起,复述各种基本概念、底层原理......嗯,看似很高级~ 但我阅读的时候,给我的感觉是,把你绕晕、把你劝退的感觉,相信大家都有同感,…

C++输入输出、缺省参数、函数重载、引用【C++初阶】

目录 一、C输入&输出 二、缺省参数 1、概念 2、分类 &#xff08;1&#xff09;全缺省 &#xff08;2&#xff09;半缺省 三、函数重载 1、概念 2、原理------名字修饰 一、C输入&输出 在C语言中&#xff0c;我们常用printf和scanf这两个函数进行输入输出。 …

产品-Axure9(英文版),.rp文件与.rplb文件的转换与区分

文章目录1、区分2、相互转换2.1 rp转为rplb2.1 rplb转为rp1、区分 rp文件是文档文件&#xff0c;可以理解为作品文件&#xff0c;自己的工作输出就是rp文件&#xff0c;图标如下。 rplb文件是库文件&#xff0c;是在制作文件过程中一个快捷库&#xff0c;图标如下 在点击绿色…

GitHub 上诞生了一个可视化低代码神器

作为开发者&#xff0c;你是否早已厌倦了日复一日的“增删改查”&#xff0c;每天都在重复造轮子&#xff0c;今天给大家推荐一款开源、靠谱、实用的低代码开发平台 -- ILLA Builder。 产品介绍 ILLA Builder 是 ILLA 的核心产品&#xff0c;是一款开源的低代码开发工具。通过…

ROS话题通信自定义+发布订阅代码--03

话题通信自定义msg 在 ROS 通信协议中&#xff0c;数据载体是一个较为重要组成部分&#xff0c;ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是&#xff0c;这些数据一般只包含一个 data 字段&#xff0c;结构的单一意味…

C++实现JPEG格式图片解析(附代码)

在网上看了好多解析JPEG图片的文章&#xff0c;多多少少都有问题&#xff0c;下面是我参考过的文章链接&#xff1a; 首先&#xff0c;解析的步骤1.读取文件的信息2.Huffman编码解码3.直流交流编码解析然而&#xff0c;读取多少个88矩阵才能解析出一个MCU呢&#xff1f;4.反量化…