当前位置: 首页 > news >正文

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

相关文章:

Go --- Go语言垃圾处理

概念 垃圾回收(GC-Garbage Collection)暂停程序业务逻辑SWT(stop the world)程序根节点:程序中被直接或间接引用的对象集合,能通过他们找出所有可以被访问到的对象,所以Go程序的根节点通常包括…...

力扣每日一题30:串联所有单词的子串

题目描述 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如,如果 words ["ab","cd","ef"], 那么 &q…...

vim | vim的快捷命令行

快捷进入shell界面 -> :nnoremap <F8> :sh<CR> -> 绑定到了F8 :nnoremap <F8> :sh<CR> 快捷执行 -> :nnoremap <F5> :wa<CR>:!g % -o a.out && ./a.out<CR> -> 绑定到了F5 :nnoremap <F5> :wa<CR>…...

项目管理平台-01-BugClose 入门介绍

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

web集群-lvs-DR模式基本配置

目录 环境&#xff1a; 一、配置RS 1、安装常见软件 2、配置web服务 3、添加vip 4、arp抑制 二、配置LVS 1、添加vip 2、安装配置工具 3、配置DR 三、测试 四、脚本方式配置 1、LVS-DR 2、LVS-RS 环境&#xff1a; master lvs 192.168.80.161 no…...

基于深度学习的面部情绪识别算法仿真与分析

声明&#xff1a;以下内容均属于本人本科论文内容&#xff0c;禁止盗用&#xff0c;否则将追究相关责任 基于深度学习的面部情绪识别算法仿真与分析 摘要结果分析1、本次设计通过网络爬虫技术获取了七种面部情绪图片&#xff1a;吃惊、恐惧、厌恶、高兴、伤心、愤怒、自然各若…...

C语言经典面试题目(十六)

1、什么是C语言中的指针常量和指针变量&#xff1f;它们有什么区别&#xff1f; 在C语言中&#xff0c;指针常量和指针变量是指针的两种不同类型。它们的区别在于指针的指向和指针本身是否可以被修改。 指针常量&#xff1a;指针指向的内存地址不可变&#xff0c;但指针本身的…...

【C语言】文件操作揭秘:C语言中文件的顺序读写、随机读写、判断文件结束和文件缓冲区详细解析【图文详解】

欢迎来CILMY23的博客喔&#xff0c;本篇为【C语言】文件操作揭秘&#xff1a;C语言中文件的顺序读写、随机读写、判断文件结束和文件缓冲区详细解析【图文详解】&#xff0c;感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注收藏。 前言 欢迎来到本篇博客&…...

JAVA八股文面经问题整理第6弹

文章目录 目录 文章目录 提问问题 问题1 问题2 问题3 问题4 问题5 问题6 问题7 问题8 问题9 问题10 问题11 问题12 写在最后 提问问题 介绍一下Linux常⽤命令&#xff0c;例如&#xff1a;Vim快捷键&#xff0c;常⽤查看Log的命令&#xff0c;路径相关&#x…...

pytest相关面试题

pytest是什么&#xff1f;它有什么优点&#xff1f; pytest是一个非常流行的Python测试框架&#xff0c;它具有简洁、易用、高校等优点。他可以帮助测试人员方便地编写和运行测试用例&#xff0c;并且提供了丰富的插件和扩展&#xff0c;支持各种测试需求介绍下pytest常用的库 …...

Keras库搭建神经网络

Keras并非简单的神经网络库&#xff0c;而是一个基于Theano的强大的深度学习库&#xff0c;利用它不仅仅可以搭建普通的神经网络&#xff0c;还可以搭建各种深度学习模型&#xff0c;如自编码器、循环神经网络、递归神经网络、卷积神经网络等。 安装代码&#xff1a; pip ins…...

适配器模式与桥接模式-灵活应对变化的两种设计策略大比拼

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自&#xff1a;设计模式深度解析&#xff1a;适配器模式与桥接模式-灵活应对变…...

Elasticsearch8搭建及Springboot中集成使用

1.搭建 1.1.下载地址 Elasticsearch&#xff1a;https://www.elastic.co/cn/downloads/elasticsearch Kibana&#xff1a;https://www.elastic.co/cn/downloads/kibana 1.2.具体过程 下载安装包&#xff1a;访问上述链接&#xff0c;下载适合你操作系统的Elasticsearch和Ki…...

asp.net在线租车平台

说明文档 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 主要技术&#xff1a; 基于asp.net架构和sql server数据库 功能模块&#xff1a; asp.net在线租车平台 用户功能有首页 行业新闻用户注册车辆查询租车介绍访问后台 后台管理员可以进行用户管理 管…...

Beamer模板——基于LaTeX制作学术PPT

Beamer模板——基于LaTeX制作学术PPT 介绍Beamer的基本使用安装和编译用于学术汇报的模板项目代码模板效果图 Beamer的高级特性动态效果分栏布局定理环境 介绍 在学术领域&#xff0c;演示文稿是展示和讨论研究成果的重要方式。传统的PowerPoint虽然方便&#xff0c;但在处理复…...

性能测试-Jmeter中IF控制器使用

一、Jmeter控制器 分为两种类型&#xff1a; 控制测试计划执行过程中节点的逻辑执行顺序&#xff0c;如&#xff1a;循环控制器&#xff0c;if控制器等对测试计划中的脚本进行分组&#xff0c;方便Jmeter统计执行结果以及进行脚本的运行时控制等&#xff0c;如&#xff1a;吞…...

华为综合案例-普通WLAN全覆盖配置(2)

组网图 结果验证 在AC_1和AC_2上执行display ap all命令&#xff0c;检查当前AP的状态&#xff0c;显示以下信息表示AP上线成功。[AC_1] display ap all Total AP information: nor : normal [1] ExtraInfo : Extra information P : insufficient power supply ---…...

这里是一本关于 DevOps 企业级 CI/CD 实战的书籍...

文章目录 &#x1f4cb; 前言&#x1f3af; 什么是 DevOps&#x1f3af; 什么是 CI/CD&#x1f3af;什么是 Jenkins&#x1f9e9; Jenkins 简单案例 &#x1f3af; DevOps 企业级实战书籍推荐&#x1f525; 参与方式 &#x1f4cb; 前言 企业级 CI/CD 实战是一个涉及到软件开发…...

机器学习 - save和load训练好的模型

如果已经训练好了一个模型&#xff0c;你就可以save和load这模型。 For saving and loading models in PyTorch, there are three main methods you should be aware of. PyTorch methodWhat does it do?torch.saveSaves a serialized object to disk using Python’s pickl…...

【动态规划】【同余前缀和】【多重背包】[推荐]2902. 和带限制的子多重集合的数目

本文涉及知识点 动态规划汇总 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 C算法&#xff1a;滑动窗口总结 多重背包 LeetCode2902. 和带限制的子多重集合的数目 给你一个下标从 0 开始的非负整数数组 nums 和两个整数 l 和 r 。 请你…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...