Go垃圾回收原理
术语介绍
赋值器:说白了就是你写的程序代码,在程序的执行过程中,可能会改变对象的引用关系,或者创建新的引用。
回收器:垃圾回收器的责任就是去干掉那些程序中不再被引用得对象。
STW:全称是stop the word,GC期间某个阶段会停止所有的赋值器,中断你的程序逻辑,以确定引用关系。即STW停止程序运行。
root对象:根对象是指不需要通过其他对象就可以直接访问到的对象,通过root对象, 可以追踪到其他存活的对象。
常见的root对:
- 全局变量:程序在编译期就能确定的那些存在于程序整个生命周期的变量(这些变量是可能引用堆上的对象或者是指针指向堆上的变量)。
- 执行栈:每个 goroutine (包括main函数)都拥有自己的执行栈,这些执行栈上包含栈上的变量(这些变量本身就可能内存逃逸到堆上或者引用堆上的变量)及指向堆内存地址的指针变量。
- 寄存器:寄存器的值可能是一个指针,而这个指针可能指向堆内存地址。
标记清除法(V1.3)
步骤
- 开启STW,从根对象开始标记对象
- 清除未被标记的对象,关闭STW
缺点
GC期间全程STW,GC和用户程序互相干扰,不能同时执行。
三色标记法
背景
基于标记清除算法的缺点,Golang团队对GC算法进行优化,减少STW以便GC和用户程序可以互不干扰,并发进行,于是就产生了三色标记法。
步骤
- GC开始前,所有对象都,都被标记为白色
- GC开始时,把所有根对象标记为灰色
- GC进行时,遍历灰色对象,把灰色对象可达的对象标记为黑色,把自己标记为黑色
- 重复第3步,直到标记完所有对象
- GC结束时,回收白色对象
缺点
上述三色标记法仍然需要STW,因为我们的应用程序会改变对象的应用关系,从而影响标记结果的正确性。
比如:
- 一个白色对象被灰色对象引用
- 此时有一个黑色对象改变引用指向这个白色对象,而灰色对象到白色对象之间的引用关系又被破坏了
- 此时这个白色对象只被一个黑色对象引用,这个白色对象不可能会标记了,因此该白色对象丢失
总结
其实总结来看,在三色标记法的过程中对象丢失,需要同时满足下面两个条件:
条件一:白色对象被黑色对象引用
条件二:灰色对象与白色对象之间的可达关系遭到破坏
看来只要把上面两个条件破坏掉一个,就可以保证对象不丢失。可以使用插入写屏障和删除写屏障来破坏上面其中一个条件。
插入写屏障
规则:当一个对象引用另外一个对象时,将另外一个对象标记为灰色。
解释:用来破坏条件一,单黑色对象引用白色对象是,白色对象就被标记成了灰色对象,就不可能出现条件一这种情况出现。
注意
插入屏障仅会在堆内存中生效,不对栈内存空间生效。这是因为go在并发运行时,大部分的操作都发生在栈上,函数调用会非常频繁。数十万goroutine的栈都进行屏障保护自然会有性能问题。
我要补充一下“生效”的具体意思:如果一个变量是在堆中如果它改变引用关系指向到另一个对象,我们把这个被指向的对象设置为灰色,如果一个变量在栈中,如果它改变引用关系指向到另一个对象,由于插入写屏障不生效,所以不用改变被引用的对象颜色为灰色
但是正是因为没有改变为灰色所以才要在GC结束时打开STW重新扫描栈如果栈和堆都使用插入写屏障,那就不用扫描重新扫描栈了,但是栈上打开STW效率太低了。
步骤
- GC开始时,堆打开插入写屏障(栈不打开)
- GC期间, 三色标记法进行标记
- GC期间,堆中对象改变它的应用关系到另外一个对象,则把这个“另外对象”给标记为灰色
- GC结束时,打开STW重新扫描栈中对象进行扫描标记
- GC结束时,进行垃圾回收
缺点:GC结束时需要,打开STW重新扫描栈,保证引用的白色对象存活(主要保证的是堆中白色对象存活和栈中变量对逃逸到堆中的白色象,这些白色对象的产生是因为栈没开插入写屏障)。
删除写屏障
规则:在删除引用时,如果被删除引用的对象自身为灰色或者白色,那么被标记为灰色。
解释:用来破坏条件二,同过把被删除引用的对象设置为灰色,把这个被删除引用的对象自己当成可达的,那么相当于可达对象(被删除引用的这个对象)到白色对象(被删除引用的这个对象原本就能遍历/访问到的其他对象)之间的可达关系又重新建立起来来。这个被删除引用的对象和它能到达的一些对象都保护了起来,因此就破坏了条件二。
步骤
- GC开始时,STW 扫描整个栈(所有协程的栈),保证所有堆上在用的对象都处于灰色保护下,保证的是弱三色不变式;
- GC期间,三色标记
- GC期间,某个对象原本引用对象A,后来改变引用到对象B,则把B对象设置为灰色
- GC结束时,进行垃圾回收
缺点
- 由于起始快照的原因,起始也是执行 STW,删除写屏障不适用于栈特别大的场景,栈越大,STW 扫描时间越长,对于现代服务器上的程序来说,栈地址空间都很大,所以删除写屏障都不适用,一般适用于很小的栈内存,比如嵌入式,物联网的一些程序;
- 并且删除写屏障会导致扫描进度(波面)的后退,所以扫描精度不如插入写屏障;
注意
在上面的GC步骤1为什么要STW 扫描整个栈(所有协程的栈)?这个问题我说一点我个人的猜测吧,毕竟网上查了半天也没有一个人能说明白的(有朋友明白原因的麻烦告知一下)
首先在说删除写屏障之前时有一个大前提就是不能在栈中使用删除写屏障,只在堆中使用。
-
当我们开启STW后把所有栈道对象都染为黑色,那么栈对象直接引用的所有堆对象都被染色成了灰色(如下图灰色部门)。
-
因此所有的(不是垃圾的)堆对象都处于灰色的保护状态中(上图蓝色和绿色部分),换句话说就是,每个堆中的对象都至少被一个灰色对象直接或间接引用。
-
当堆中的引用关系发生变化时,根据删除写屏障把被改变引用的那个堆对象变成灰色,就能一定破坏“条件二了”
我们举个反向例子,初始状态,有2个协程栈:
A 是 g1 栈的一个对象,g1栈已经扫描完了,并且 C 也是扫黑了的对象
B 是 g2 栈的对象,指向了 C 和 D,g2 完全还没扫描,B 是一个灰色对象,D 是白色对象
步骤一:g2 进行赋值变更,把 C 指向 D 对象,这个时候黑色的 C 就指向了白色的 D(由于是删除屏障,这里是不会把D染色为灰色)
步骤二:把 B 指向 D 的引用删除,由于是栈对象操作,不会触发删除写屏障(这里我们讨论的大前提是对栈是不使用删除写屏障的,即使把B到D引用关系删除了也不会把D染成灰色);
步骤三:GC结束时,因为 C 已经是黑色对象了,所以不会再扫描,所以 D 仍然为白色,就会被错误的清理掉。
总结
那么如果我不想一次把所有的协程栈都暂停,就会产生上面例子中的问题,有什么办法可以解决上面的例子中的问题吗(在不同时暂停所有协程栈道情况下)?这个当然是有的。
解决办法就是:使用三色标记法+插入写屏障+删除写屏障
需要注意:混合写屏障扫描栈虽然没有 STW,但是扫描某一个具体的栈的时候,还是要停止这个 goroutine 赋值器的工作的哈(针对一个 goroutine 栈来说,是暂停扫的,要么全灰,要么全黑哈,原子状态切换)
我们根据上面的这幅图可以看出只要我们在目前的GC策略上再添加上插入写屏障(当前的GC策略是三色标记法+删除写屏障),在C引用D时直接把D染为灰色,就能解决所有问题了。
三色标记法+插入写屏障(V1.5)
插入写屏障机制和删除写屏障机制中任一机制均可保护对象不被丢失。在V1.5的版本中采用的是插入写机制实现。
三色标记法+混合写屏障(V1.8)
背景
从上面的分析中,我可以知道:
插入写屏障,可以做用户程序和GC同时运行,什么都好就是要在GC结束时重新扫描栈中的根对象防止堆中的变量被释放
删除写屏障,也可以做用户程序和GC同时运行,但是需要在GC开始前把所有的协程栈暂停,这对协程数量非常多的时候是不可接受的。
那么有没有一种方法,在GC开始的时候不需要STW 扫描整个栈(把整个栈中的对象标记为黑色),在程序结束时不需要再次重新扫描栈以防止对象丢失,并且GC程序可以和用户程序同时运行?
这种方法就是我们的三色标记法+混合写屏障了
步骤
- GC开始时优先扫描将栈,将栈上可达对象标记为黑色。扫描某个 goroutine 时停止这个 goroutine 赋值器的工作。即goroutine看来是原子操作,瞬间全灰/黑。栈扫描完成后解锁。
- GC期间栈上新建的对象都为黑色
- 堆上被删除的对象标记为灰色
- 堆上新添加的对象标记为灰色
注意:写屏障(插入、删除写屏障)只在堆上启用,栈上不开启写屏障
优点
- 不用在开始时像删除写屏障那样,需要同时STW所有协程来标记根栈上的根对象
- 不用在结束时像插入写屏障那样,需要再次打开STW重新扫描栈,保证引用的白色对象存活
相关文章:

Go垃圾回收原理
术语介绍 赋值器:说白了就是你写的程序代码,在程序的执行过程中,可能会改变对象的引用关系,或者创建新的引用。 回收器:垃圾回收器的责任就是去干掉那些程序中不再被引用得对象。 STW:全称是stop the word,GC期间某个阶段会停止…...
Coredump-N: stack 空间被临时变量吃满,导致内存访问出现问题
文章目录 代码寄存器汇编代码 int main() {fun(0); #define S 0x0019fd08UL 、、 乘5 等0x81F128 char buff4[S]; char buff3[S]; char buff2[S]; char buff1[S]; char buff[S]; memset(buff, 0, sizeof(buff)); memset(buff4, 0, sizeof(buff)); memset(buff3, 0, sizeof(buf…...
GO中使用viper读取配置文件
文章目录 viper的使用例子一:例子二:viper的使用 viper的源码地址https://github.com/spf13/viper,它是一个可以用来读取配置文件的工具。在项目中可以通过下面指令安装: go get github.com/spf13/viper 下面我们通过两个例子,来介绍一下viper在项目中应该如何使用…...

webpack dll 提升构建速度
DLL,动态链接库(Dynamic Link Library 或者 Dynamic-link Library),由微软公司提出。目的是为了节约应用程序所需的磁盘和内存空间。 在一个传统的非共享库中,如果两个程序调用同一个子程序,就会出现两份那…...

C++面向对象编程之三:初始化列表、类对象作为类成员、静态成员
初始化列表C提供了初始化列表语法,可以用于成员属性初始化。语法规则:无参构造函数():属性1(值1), 属性2(值2), ... { }有参构造函数(形参1, 形参2, ...):属性1(形参1), 属性2(形参2), ... { }example:写一个怪物类,有怪物id和血量…...

跨域问题解决方案
目录 1.同源策略 2.解决方案(后端) (1)在后端方法添加CrossOrigin (2)添加CORS过滤器 (3)实现WebMvcConfigure接口,重写addCorsMappings方法 3.解决方案(前端) (1)前端配置代理 1.同源策略 同源策略(Same origin policy)是一种约定&am…...
Vue3电商项目实战-购物车模块7【20-登录后-批量删除、21-登录后-选中状态修改数量、22-登录后-全选反选、23-登录后-修改规格、24-下单结算】
文章目录20-登录后-批量删除21-登录后-选中状态&修改数量22-登录后-全选反选23-登录后-修改规格24-下单结算20-登录后-批量删除 目标:完成批量删除选中商品,完成清空失效的商品 大概步骤: 完成cart.js模块中的批量删除actions的登录状态…...

软件测试之快速熟悉项目
快速熟悉项目 1、了解项目架构 C/S架构 C/S 代表的是客户端/服务器(client/server),这类软件的使用者需要在本地电脑安装客户端程序,例如:QQ。 优点:安全性高。 缺点:一旦软件有更新,用户需要手动下载&am…...
软考高级信息系统项目管理师系列之二十一:项目风险管理
软考高级信息系统项目管理师系列之二十一:项目风险管理 一、项目风险管理内容整理二、项目风险管理1.风险及项目风险管理定义2.项目风险的特点3.风险的分类4.风险成本5.项目风险管理与其他管理的关系三、规划风险管理1.规划风险管理2.输入3.工具与技术4.输出四、识别风险1.识别…...

打包成JAR文件和WAR文件,到底有什么区别?
Spring Boot是一种基于Spring框架的快速开发应用程序的工具,可以轻松地构建可部署的独立应用程序。在使用Spring Boot时,你可能会注意到有两种不同的部署选项:打包成JAR文件和WAR文件。在这篇文章中,我们将深入探讨这两种部署选项…...

STM32 OTA应用开发——通过串口/RS485实现OTA升级(方式1)
STM32 OTA应用开发——通过串口/RS485实现OTA升级(方式1) 目录STM32 OTA应用开发——通过串口/RS485实现OTA升级(方式1)前言1 环境搭建2 功能描述3 程序编写3.1 BootLoader部分3.2 APP的制作4 修改工程中的内存配置4.1 Bootloader…...

在教学中常被问到的几个vue3.x与typescript的问题,统一解答
在教学当中,学生在学习vue3.x时,常常会问到typescript和vue3.x之间的关系,感觉这两个技术总是绑在一起的,下面老赵来统一解答一下: 那学vue3.x,为什么要求也要掌握typescript Vue 3.x是一个使用TypeScript编…...
纯css实现超炫酷的星空背景按钮
也是在制作项目时发现的,找了很多demo,一点一点测试,发现这个按钮也是非常的炫酷 用到了几个属性,keyframes,::after,::before 先了解一下他们分别都是干嘛的 keyframes 关键帧 keyframes at-rule 规则通过在动画序…...

openpnp - 贴片前, 放入一块新板子后, 对板子的坐标矫正
文章目录openpnp - 贴片前, 放入一块新板子后, 对板子的坐标矫正概述笔记实验前置条件实验开始建立自己板子上的Mark点封装, 用于自己人工圈定判断Mark点位置是否正确建立mark点封装根据多个mark点, 来精确定位板子左下角原点坐标ENDopenpnp - 贴片前, 放入一块新板子后, 对板子…...

计及需求响应的改进灰狼优化算法求解风、光、柴、储容量优化配置(Matlab代码实现)
👨🎓个人主页:研学社的博客💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密…...

Elasticsearch使用——高级篇
1.数据聚合**聚合(aggregations)**可以让我们极其方便的实现对数据的统计、分析、运算。例如:什么品牌的手机最受欢迎?这些手机的平均价格、最高价格、最低价格?这些手机每月的销售情况如何?实现这些统计功…...
Java网络爬虫-HttpClient工具类
关于用Java进行爬虫的资料网上实在少之又少,但作为以一名对Java刚刚初窥门径建立好兴趣的学生怎么能静得下心用新学的Python去写,毕竟Java是世界上最好的语言嘛 (狗头)关于Java爬虫最受欢迎的一个框架Jsoup常常搭配HttpClient来使用,因为Jsou…...

LeetCode203_203. 移除链表元素
LeetCode203_203. 移除链表元素 一、描述 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val val 的节点,并返回 新的头节点 。 示例 1: 输入:head [1,2,6,3,4,5,6], val 6 输出:[1,2…...
【洛谷 P1443】马的遍历 题解(广度优先搜索)
马的遍历 题目描述 有一个 nmn \times mnm 的棋盘,在某个点 (x,y)(x, y)(x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。 输入格式 输入只有一行四个整数,分别为 n,m,x,yn, m, x, yn,m,x,y。 输出格式 一个 nmn \t…...
为什么gpt输出有随机性?
以下答案由chatGPT产生! 为什么gpt输出有随机性? GPT(Generative Pre-trained Transformer)是一种基于Transformer架构的神经语言模型,它是一个深度学习模型,通过在大规模文本数据上进行预训练࿰…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
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…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...