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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UE5的渲染-太难了

大家可以看到,这些都是UE的渲染,非常漂亮惊叹,渲染已经非常成熟,这些画面并不是离线渲染,而是实时渲染。早先年我们渲染CG动画都采用离线渲染,要用到庞大的渲染农场,每渲染一帧都可能需要半个小…

【Maven】使用maven-jar、maven-assembly、maven-shade优雅的实现第三方依赖一同打Jar包

文章目录 一.前言二.常规Jar 打包:maven-jar-plugin三.Shade 打包:maven-shade-plugin1.如何使用2.将部分jar包添加或排除3.将依赖jar包内部资源添加或排除4.自动将所有不使用的类排除5.将依赖的类重命名并打包进来 (隔离方案)6.修…

VMware中UbuntuServer扩展硬盘空间

VMware中UbuntuServer扩展硬盘空间 没有不可治愈的伤痛,没有不能结束的沉沦,所有失去的,会以另一种方式归来 ——【约翰-肖尔斯】 第一步 lxalxa:~$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS loop0 …

从边缘设备丰富你的 Elasticsearch 文档

作者:David Pilato 我们在之前的文章中已经了解了如何丰富 Elasticsearch 本身和 Logstash 中的数据。 但如果我们可以从边缘设备中做到这一点呢? 这将减少 Elasticsearch 要做的工作。 让我们看看如何从具有代理处理器的 Elastic 代理中执行此操作。 E…

JDK,Nginx,Redis安装

创建develop目录 mkdir /usr/local/develop/ cd /usr/local/develop 下载 wget http://nginx.org/download/nginx-1.17.4.tar.gz yum install git git clone https://github.com/arut/nginx-rtmp-module.git 解压文件 tar zxmf nginx-1.17.4.tar.gz 进入解压目录 cd ng…

flutter 局部view更新,dialog更新进度,dialog更新

局部更新有好几种方法,本次使用的是 StatefulBuilder 定义 customState去更新对话框内容 import package:flutter/cupertino.dart; import package:flutter/material.dart;class ProgressDialog {final BuildContext context;BuildContext? dialogContext;double _…

关系型数据库mysql(2)SQL语句

目录 一.SQL语句简介 1.1SQL语言 1.2SQL语句分类 1.3SQL分类 1.4SQL 语言规范 二.数据库基本操作 2.1查看数据库中的库信息 2.2查看数据库中的表信息 数据库内查看 数据库外查看 2.3显示数据库的结构(字段) ​编辑 2.4 字段属性 2.5常见的数…

音频和视频标签

音频用audio标签 controls表示控制栏 loop循环播放音频 autoplay自动播放&#xff08;浏览器基于隐私一般不支持&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Com…

sonar接入maven项目

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

[BT]BUUCTF刷题第2天(3.20)

第2天&#xff08;共5题&#xff09; Web [ACTF2020 新生赛]Exec Payload&#xff1a;target127.0.0.1;cat /flag 分号;在许多shell中用作命令分隔符&#xff0c;意味着在执行完前一个命令&#xff08;这里是设置target变量&#xff09;后&#xff0c;接着执行cat /flag命令…

Spark-Scala语言实战(4)

在之前的文章中&#xff0c;我们学习了如何在scala中定义无参&#xff0c;带参以及匿名函数。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 Spark-Scala语言…

IDEA 下载依赖包源码报错 Cannot download sources Sources not found for:XXX

最近在做一个功能的时候想看一个库的源码&#xff0c;结果源码下不下来&#xff0c;报Cannot download sources Sources not found for:XXX,网上搜了半天&#xff0c;也找不到靠谱的结论 后来想了下&#xff0c;应该是镜像那边出了问题&#xff0c;把镜像一删&#xff0c;源码…

航空实时监控

1、从Kafka中读取飞机数据&#xff0c;并进行清洗 此步骤在前面的“使用Spark清洗统计业务数据并保存到数据库中”任务阶段应该已经完成。如果没有完成&#xff0c;请参考源代码自行完成。核心类主要有三个&#xff1a;SparkStreamingApplication类、SparkUtil类和MapManager类…

免费PDF转换和编辑工具 PDFgear 2.1.4

PDFgear是一款功能强大的 PDF 阅读及转换软件。 它支持多种文件格式的转换和编辑&#xff0c;同时还提供了丰富的功能模块&#xff0c;如签名、表单填写等&#xff0c;方便用户进行多样化的操作。 该软件界面简洁美观&#xff0c;操作简单易懂&#xff0c;适合不同层次的用户…

从零到一构建短链接系统(八)

1.git上传远程仓库&#xff08;现在才想起来&#xff09; git init git add . git commit -m "first commit" git remote add origin OLiyscxm/shortlink git push -u origin "master" 2.开发全局异常拦截器之后就可以简化UserController 拦截器可以…

ASP .Net Core 配置集合 IConfiguration 的使用

&#x1f433;简介 IConfiguration 是 ASP.NET Core 中的一个接口&#xff0c;用于表示配置集合。以下是关于 IConfiguration 的详细介绍&#xff1a; 作用&#xff1a;IConfiguration 允许开发人员从各种来源&#xff08;如文件、环境变量、命令行参数等&#xff09;读取应用…

linux 命令笔记:gpustat

1 命令介绍 gpustat是一个基于Python的命令行工具&#xff0c;它提供了一种快速、简洁的方式来查看GPU的状态和使用情况它是nvidia-smi工具的一个封装&#xff0c;旨在以更友好和易于阅读的格式显示GPU信息。gpustat不仅显示基本的GPU状态&#xff08;如温度、GPU利用率和内存…

软考高级:软件架构风格-虚拟机风格概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

ubuntu部署wireguard服务端,ubuntu部署wireguard客户端

docker部署方式 docker run -d \--namewg-easy \-e WG_HOST6.6.6.6服务端IP \-e PASSWORD123abc登陆管理密码 \-e WG_DEFAULT_ADDRESS10.0.8.x客户端 IP 地址范围 \-e WG_DEFAULT_DNS1.1.1.1配置dns \-e WG_ALLOWED_IPS10.0.8.0/24 \-e WG_PERSISTENT_KEEPALIVE25 \-v ~/.wg-e…

汽车KL15、KL30、ACC的区别

文章目录 前言一、KL30是什么&#xff1f;二、KL15是什么&#xff1f;KL15信号的演变 三、为啥用KL15、KL30呢&#xff1f; 前言 相信刚接触汽车电子的伙伴都会有一个疑惑&#xff0c;什么是KL15?什么是KL30? 内心一脸懵逼…… KL是德语Klemme的缩写&#xff0c;指的是ECU的…