Go --- Go语言垃圾处理

概念

  • 垃圾回收(GC-Garbage Collection)
  • 暂停程序业务逻辑SWT(stop the world)
  • 程序根节点:程序中被直接或间接引用的对象集合,能通过他们找出所有可以被访问到的对象,所以Go程序的根节点通常包括以下几个对象
    • 程序的全局变量和静态变量
    • 程序的调用栈中的变量
    • 当前执行的goroutine

在这里插入图片描述

知识点

创建的值在物理内存中,但是物理内存终归是有限的所以需要有垃圾回收(GC)机制。Go语言标准工具链为每一个应用程序都提供了一个runtime库,这个runtime库包含一个垃圾收集器。

为什么要有垃圾回收机制。

go里面的值存在了哪里?

存储在局部变量里的非指针的Go值通常不交由Go GC 管理,Go 将安排分配与创建它的词法范围相关的内存,这种内存分配方式往往比Go GC管理来的更有效,因为Go编译器可以知道预先知道何时释放内存和清除内存。这种分配方式被称为“栈分配”(stack allocation),因为变量存放在goroutine栈空间上。

Go编译器无法确定其生命周期,无法以这种方式分配内存的Go值被称为逃逸到堆(escape to the heap),可以将堆看成是一个内存分配的大包裹,当Go值需要分配内存时,就往里边装。在堆上进行内存分配的行为为动态内存分配,这种类型的内存分配不会假设Go值合适使用和何时释放。如此需要清理这些内存就需要用到Go GC——专门识别和清理动态内存分配的系统了。

虽然GC并不管理栈上的内存,但是扔然需要扫描调用栈中的变量,以确保不会回收被其他对象引用的变量

为什么Go值需要逃逸到堆,一个可能的原因是它的大小是动态决定的,如切片(slice)。需要注意的是,这个逃逸是传递的,如果一个Go值由一个已经逃逸到堆的Go值引用,那这个也肯定是逃逸的。

Go值是否逃逸需要依据上下文和Go编译器的逃逸算法,这个算法非常复杂,而且会随着版本更迭。如果想要更多的了解可以去看 eliminating heap allocations。

在这里插入图片描述

追踪垃圾回收

垃圾回收也许指的是不同自动回收内存的方法,如引用计数。但本文档介绍的是垃圾回收指的是跟踪垃圾回收,他通过跟随指针来识别正在使用的、活动的对象。

首先让我们先明确一下定义:

对象——对象是一块动态分配的内存,包含一个或多个 Go 值

指针——引用对象内任何值的内存地址。这自然包括 *T 形式的 Go 值,但也包括部分内置 Go 值。如字符串、切片、通道、映射和接口值都包含 GC 必须跟踪的内存地址。

对象和指向其他对象的指针一起形成对象图,为了识别实时内存,GC 从程序的根部开始遍历对象图,这些指针标识程序肯定正在使用的对象。根的两个例子是局部变量和全局变量。遍历对象图的过程称为扫描

这个基本的算法是所有追踪GC所共有的,不同追踪GC的不同之处在于一旦发现内存处于活跃状态,他们会采取什么措施。就Go GC而言,他采用的是标记-清除技术,这意味着为了跟踪其进度,GC会将其遇到得值标记为活跃状态。等待跟踪完毕后,GC会遍历堆中的所有内存,并使所有未标记的为可供分配的内存。这个过程称为清除

你可能熟悉的一种替代技术是将对象实际移动到内存的新部分并留下转发指针,该指针稍后用于更新所有应用程序的指针。我们将这种移动对象的GC称为移动GC; Go 有一个不动的 GC。

GC 周期

由于Go GC是标记-清除GC,因此它大致可以分为两部分运行:标记阶段和清除阶段,在进行标记时可能仍有未标记的对象被引用而处在活跃状态,所以清除行为必须是与标记行为完全分开的。此外,当没有GC相关工作要做时,GC也可能根本不活动。GC在所谓的GC周期中不断的轮流执行清除、关闭和标记这三个阶段。处于本文档的目的,请考虑从清除、关闭、然后标记开始的GC循环。

接下来的几节将重点关注建立对 GC 成本的直觉,以帮助用户根据自己的利益调整 GC 参数。

// 后面的区域以后再来探索吧。原文地址,需要小猫。

https://go.dev/doc/gc-guide

算法详情

不同版本的GC算法不同:

  • V1.3 标记-清除算法
  • V1.5 三色并发标记法
  • V1.8 混合屏障机制

标记清除算法

上面已经介绍了。

具体步骤

第一步:开始SWT,暂停程序业务逻辑, 分类出可达和不可达的对象,然后做上标记

第二步:开始标记,程序找出它所有可达的对象,并做上标记。

第三步: 标记完了之后,然后开始清除未标记的对象。

第四步:停止SWT,让程序继续跑。

缺点(不足):

标记算法清晰明了,但是有非常严重的问题。

  • 首先是STW,会让程序暂停,让运行出现卡顿
  • 在标记阶段会扫描整个堆。
  • 清除数据会产生堆碎片

虽然做了简单的优化(将清除阶段(第三步)和停止SWT(第四步)交换位置),但是优化后的算法仍然存在最大的问题就是使用了STW,算法会暂停整个程序。

三色并发标记法

所谓的三色标记法就是使用三种不同的状态来标记对象,根据对象状态的不同来确定需要清除的对象有那些。

具体步骤

第一步:新创建的对象,默认的颜色都是标记为“白色”。

第二步:开始从程序根节点开始遍历对象,找到可达对象,将可达对象从白染灰。注意:不是一下子将所有白色节点都遍历了,只是找到了根节点可达的白色节点。

第三步:遍历灰色节点找到可达对象,将可达对象染灰,然后该灰色节点染黑。

第四步:重复第三步,直到所有灰色节点均无可达对象,然后将所有灰色节点染黑。

第五步:回收所有白色节点(这些都是程序不可达的对象,是需要被清理的对象),省下的就只剩程序可达的黑色节点。

根据上述步骤,我们并没有发现他解决了需要引入STW而导致程序卡顿的问题。因为在遍历灰色节点寻找可达对象时为了保证数据安全还是会选择开始三色标记之前就加上STW。

为什么非要使用STW不可呢?

接下来我们分析一下以下情况。

一、当GC在三色标记的标记阶段,程序运行中出现黑色节点的对象引用了白色节点的对象,当然如果该白色节点恰好是可达对象,只是还没被GC扫描到哪还好说,但是如果该白色节点恰好是程序不可达对象,就会出现引用的对象在扫描结束后被清除的情况。一个被引用的对象就这样被清除了。

二、在标记过程中,一个灰色节点指向一个白色节点的引用指针被运行中的程序移除,同时一个黑色节点多了一个指向该白色节点的引用指针,等标记过后,发现该白色指针并没有被标记为黑色。一个被引用的对象就这样被清除了。

为了避免这两种情况出现,最简单的方法就是在标记之前启动STW,清除过后停止STW,但是会较大程度上影响程序的运行。在上面两种情况对应了两个条件。

一、不希望黑色节点后面直接连接白色节点

二、不希望灰色节点在黑色节点连接白色节点后被清除了与该白色节点的可达关系

因为我们并不希望使用STW,那么有什么方法能破坏这两个条件呢?

屏障机制

了解屏障机制之前需要先了解两个概念:

一、强三色不变式:不存在黑色节点引用到白色节点的指针。强制性不允许黑色节点指向白色节点。

二、弱三色不变式:所有被黑色节点引用的白色节点都处于灰色保护状态即被引用的白色节点终会在标记阶段变为可达对象。

接下来在来看两种屏障机制:

一、插入写屏障:

​ 具体操作:在A对象引用B对象的时候,B对象被标记为灰色。(将B挂在A下游,B必须被标记为灰色)

​ 满足强三色不变式:因为引入的对象都是黑色或灰色(白色节点变为灰色节点)。

黑色节点所在的内存空间有两个地方,一个是栈,一个是堆。栈的空间较小,但是要速度快,要负责函数的频繁调用,因此不在栈空间上使用插入屏障(不保障满足强三色不变式,黑色节点可以指向白色节点)。插入屏障只在堆空间上使用。

尽然这样就出现一个问题,在进行一次标记后,不能确保白色节点没有被引用。所以要对栈重新进行三色标记扫描, 但这次为了对象不丢失, 要对本次标记扫描启动STW暂停. 直到栈空间的三色标记结束。

虽然还是使用了STW,不过相比于标记整个程序的内存空间,只标记栈上的空间有了很明显的效率提升。

二、删除写屏障

具体操作:被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。

满足弱三色不变式:保护灰色对象到白色对象的路径不会断。

这样也会有问题,比如一次GC周期不会删除GC启动时的可达但是过程中变为不可达的对象。

虽然引入了屏障机制,我们发现还是有不足之处:

  1. 插入写屏障:结束时需要STW来重新扫描栈。
  2. 删除写屏障:回收精度低,GC开始时STW扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象。

接下来就看看GoV1.8是如何处理的

混合写屏障(hybrid write barrier)机制

规则:

1、GC扫描栈空间上的可达对象,并全部染为黑色。(之后不再进行第二次重复扫描,无需STW)。

2、GC期间,任何在栈上创建的新对象,均为黑色。

3、GC期间,被删除的对象标记为灰色。

4、GC期间,被添加的对象标记为灰色。

满足变相的弱三色不定式。

注意:混合写屏障也不在栈空间上使用,因为栈要求的是运行速率。

只有在GC执行时才会有屏障规则,普通条件下并不使用。

Golang中的混合写屏障满足弱三色不变式,结合了删除写屏障和插入写屏障的优点,只需要在开始时并发扫描各个goroutine的栈,使其变黑并一直保持,这个过程不需要STW,而标记结束后,因为栈在扫描后始终是黑色的,也无需再进行重新扫描操作了,减少了STW的时间。

对比:

GoV1.3- 普通标记清除法,整体过程需要启动STW,效率极低。

GoV1.5- 三色标记法, 堆空间启动写屏障,栈空间不启动,全部扫描之后,需要重新扫描一次栈(需要STW),效率普通

GoV1.8-三色标记法,混合写屏障机制, 栈空间不启动,堆空间启动。整个过程几乎不需要STW,效率较高。

参考文章

语雀-Golang修养之路-刘丹冰Aceld
A Guide to the Go Garbage Collector
知乎-GC 机制中,所谓的“根节点”具体指的是什么?-orionnnn

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

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

相关文章

完全理解ARM启动流程:Uboot-Kernel

内容共计5W字数,但是我还是很多地方说的不够尽兴。那么下次聊! 前言 bootloader是系统上电后最初加载运行的代码。它提供了处理器上电复位后最开始需要执行的初始化代码。 PC机上引导程序一般由BIOS开始执行,然后读取硬盘中位于MBR(Main Bo…

openGauss学习笔记-247 openGauss性能调优-SQL调优关键参数调整

文章目录 openGauss学习笔记-247 openGauss性能调优-SQL调优关键参数调整247.1 SQL调优关键参数调整 openGauss学习笔记-247 openGauss性能调优-SQL调优关键参数调整 247.1 SQL调优关键参数调整 本节将介绍影响openGauss SQL调优性能的关键数据库主节点配置参数,配…

【JVM】为对象分配内存的方式,死亡对象判断方法

目录 为对象分配内存的方式 指针碰撞 空闲列表 TLAB 死亡对象判断方法 引用计数法 可达性分析算法 为对象分配内存的方式 指针碰撞 一般情况下,JVM的对象都放在堆内存中(发生逃逸分析除外)。当类加载检查通过后,JVM为新生…

python 中怎样使用任意关键词实参?

在 Python 中,可以使用任意数量的关键字实参和任意关键字实参,也被称为 kwargs。 这允许你在函数调用时传递任意数量的关键字参数。 你可以使用任意数量的关键字实参(Keyword Arguments)和任意关键字实参(Arbitrary Ke…

sonar接入maven项目

1、介绍 sonar是一款静态代码质量分析工具,支持Java、Python、PHP、JavaScript、CSS等25种以上的语言,而且能够集成在IDE、Jenkins、Git等服务中,方便随时查看代码质量分析报告。他有如下特性 (1) 检查代码是否遵循编程标准:如命…

走迷宫---dfs在矩阵图里的应用模板

题目描述如下: dfs算法解决迷宫问题的一个标准模板 ,通过递归与回溯暴力遍历所有能走的点,并比较找出所有可行方案的最优解 解决这道问题的核心思想和组合数如出一辙,可以说是组合数的升级版 结合注释看dfs更清晰易懂&#xff0…

springcloud-Eureka注册中心

如果你要理解这个技术博客博客专栏 请先学习以下基本的知识: 什么是微服务什么是服务拆分什么是springcloud Springcloud为微服务开发提供了一个比较泛用和全面的解决框架,springcloud继承了spring一直以来的风格——不重复造轮子,里面很多的…

测试平台——前端框架

一、创建vue项目 npm init vitelatest web_class wylWYLdeMacBook-Air testplatform % npm init vitelatest web_class ✔ Select a framework: › Vue ✔ Select a variant: › JavaScriptScaffolding project in /Users/wyl/workspace/testplatform/web_class...Done. Now…

IoT 物联网场景中如何应对安全风险?——青创智通

工业物联网解决方案-工业IOT-青创智通 ​随着物联网(IoT)技术的快速发展,越来越多的设备、系统和应用被连接到互联网上,从而构建了一个庞大的物联网生态系统。然而,这种连接性也带来了前所未有的安全风险。在物联网场景…

Android Studio实现内容丰富的安卓校园公告助手

获取源码请点击文章末尾QQ名片联系,源码不免费,尊重创作,尊重劳动 093校园助手 1.开发环境 android stuido3.6 jak1.8 eclipse mysql tomcat 2.功能介绍 具体往下看第三节,功能截图 安卓端: 1.注册登录 2.校园公告列表…

持续集成平台 02 jenkins plugin 插件

拓展阅读 Devops-01-devops 是什么? Devops-02-Jpom 简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件 代码质量管理 SonarQube-01-入门介绍 项目管理平台-01-jira 入门介绍 缺陷跟踪管理系统,为针对缺陷管理、任务追踪和项目管理的商业…

【pip 安装pymssql报错】 Failed to build pymssql

在使用pip install pymssql安装pymssql时报如下图的错误&#xff1b; 报错截图 2&#xff09;查找资料说pip<3.0版本 &#xff0c;我也试了&#xff0c;不行。 你们也可以试一试&#xff1a;pip install"pymssql<3.0" 3&#xff09;我的成功方式&#xff1…

浅谈 电脑和车的对比

https://www.zhihu.com/question/547115488 电脑CPU与汽车发动机有哪些相同点与不同点&#xff1f; - 知乎 就想机械硬盘一样 我们的技术可能达不到 但是我们可以弯道超车 比如长江存储的SSD可以取代 以前的机械硬盘

流畅的 Python 第二版(GPT 重译)(八)

第十五章&#xff1a;关于类型提示的更多内容 我学到了一个痛苦的教训&#xff0c;对于小程序来说&#xff0c;动态类型很棒。对于大型程序&#xff0c;你需要更加纪律严明的方法。如果语言给予你这种纪律&#xff0c;而不是告诉你“嗯&#xff0c;你可以做任何你想做的事情”&…

WordPress Wholesale Market 插件 任意文件读取漏洞复现

0x01 产品简介 WordPress plugin Wholesale Market 是一个woocommerce扩展插件&#xff0c;使您的商店能够创建批发用户&#xff0c;并通过设置产品的批发价格。 0x02 漏洞概述 WordPress plugin Wholesale Market 2.2.1之前版本存在路径遍历漏洞&#xff0c;该漏洞源于没有…

海南省月降水量分布数据

海南省位于中国南端&#xff0c;海南岛地处热带北缘&#xff0c;属热带季风气候&#xff0c;长夏无冬。海南省雨量充沛&#xff0c;年降水量在1000毫米&#xff5e;2600毫米之间&#xff0c;年平均降水量为1639毫米&#xff0c;有明显的多雨季和少雨季。每年的5&#xff5e;10月…

鸿蒙Harmony应用开发—ArkTS-转场动画(组件内隐式共享元素转场)

geometryTransition用于组件内隐式共享元素转场&#xff0c;在组件显示切换过程中提供平滑过渡效果。通用transition机制提供了opacity、scale等转场动效&#xff0c;geometryTransition通过id绑定in/out组件(in指入场组件、out指出场组件)&#xff0c;使得组件原本独立的trans…

RabbitMQ--03--SpringAMQP(SpringBoot集成RabbitMQ)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 SpringAMQP1.SpringBoot 的支持https://spring.io/projects/spring-amqp 2.RabbitTemplate3.RabbitListener&#xff08;终极监听方案&#xff09;4.RabbitConfig--…

基于Springboot的防疫物资管理信息系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的防疫物资管理信息系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

IOS/Android App备案(uniapp)

IOS/App备案 IOS备案Android备案 IOS备案 准备好p12证书即可 链接: https://aitoolnav.caichuangkeji.com/#/AppMd5 Android备案 上DCLOUD开发者中心&#xff0c;找到相关应用后&#xff0c;直接查看证书即可获取到MD5 公钥&#xff1a;先根据上述页面下载证书&#xff0c;…