【Golang】GC探秘/写屏障是什么?

之前写了 一篇【Golang】内存管理 ,有了很多的阅读量,那么我就接着分享一下Golang的GC相关的学习。

由于Golang的GC机制一直在持续迭代,本文叙述的主要是Go1.9版本及以后的GC机制,该版本中Golang引入了 混合写屏障大幅度地优化了STW的时间。具体GC机制的版本迭代可以参考 :GC的过去、现在和未来

基本概念

GC的对象

不再被需要的内存块,如果无法回收这部分内存将无法重复使用

内存泄漏

不再被需要的内存块,未在预期时间以内被回收的称为内存泄漏

常见的GC方法:

1.【PHP,Swift,Python等】引用计数法:

  • 每个对象维护一个自身被引用的次数,当一个引用者消息后该计数-1;
  • 它的优势为GC时查找需要回收的对象很简单:只需要找到引用计数为0的对象即可;
  • 它的劣势为在写入引用关系时需要额外维护引用计数,带来一定开销 ;
    2.【Golang】标记清除法:
  • 每次GC时通过从根对象出发递归地查询所有对象的引用关系,将无引用的对象进行标记后再行清理。
  • 它的优势在于写入引用关系时无额外开销
  • 它的劣势在于GC时查询引用关系有一定开销 ,有时还需要STW从而影响性能
STW: Stop the world

为了避免在GC的过程中,对象之间的引用关系发生新的变更使得GC的结果发生错误(比如GC过程中新增了一个引用,但是由于未扫描到该引用导致将被引用的对象清除了),停止所有正在运行的协程。
STW对性能有一些影响,Golang目前已经可以做到1ms以下的STW。

三色标记法

Golang中的GC方式为标记后清除,所采用的逻辑为——三色标记法:一开始全部的对象都是白色的


三色标记法
  1. 从root对象开始(全局变量+全局栈+当前活跃的goroutines中的栈),将它们加入到灰色队列中,进行遍历:类似于二叉树使用队列进行BFS遍历的过程
  2. 从灰色队列中pop出一个对象
  3. 将其引用的对象都置为灰色,加入到灰色队列中
  4. 将该对象置为黑色
  5. 直到灰色队列为空时,剩余的无引用的白色对象即会被清除

内存回收

看不太明白的可以参考我的上一篇文章:【Golang】内存管理

  1. 被回收的内存块会被回收到MCache(Processor私有内存)结构中 ,对应的MSpan(块类型)的allocBits的bitmap中对应的位置会被从1改为 0
  2. MSpan中除了allocBits还有另一个bitmap:GCMarkBits用于存放需要清除的标记:黑色=1,白色=0,一次GC之后所有MSpan中的GCMarkBits和AllocBits应该完全相同,因为只有黑色的内存对象在GC之后才会保留下来

写屏障

上文有提到为了避免GC的过程中新修改的引用关系到GC的结果发生错误,我们需要进行STW。但是STW会影响程序的性能,所以我们要通过写屏障技术尽可能地缩短STW的时间。

造成引用对象丢失的条件
  1. 一个黑色的节点A新增了指向白色节点C的引用
  2. 并且白色节点C没有除了A之外的其他灰色节点的引用,或者存在但是在GC过程中被删除了

以上两个条件需要同时满足:满足条件1时说明节点A已扫描完毕,A指向C的引用无法再被扫描到;满足条件2时说明白色节点C无其他灰色节点的引用了,即扫描结束后会被忽略 。

写屏障破坏两个条件其一即可
  1. 破坏条件1 => Dijistra写屏障
  • 满足强三色不变性:黑色节点不允许引用白色节点
  • 当黑色节点新增了白色节点的引用时,将对应的白色节点改为灰色
  1. 破坏条件2 => Yuasa写屏障
  • 满足弱三色不变性:黑色节点允许引用白色节点,但是该白色节点有其他灰色节点间接的引用(确保不会被遗漏)
  • 当白色节点被删除了一个引用时,悲观地认为它一定会被一个黑色节点新增引用,所以将它置为灰色
写屏障的分类

注意:由于栈上的操作需要保证性能,所以所有的写屏障均只针对堆上的对象。

写屏障的分类

GC的时机

  1. 每次内存分配时检查当前内存分配量是否已达到阈值(环境变量GOGC):默认100%,即当内存扩大一倍时启用GC
  2. 定时触发:当最近2分钟未触发过GC时,会触发一次GC
  3. 通过runtime.GC()手动触发

GC的优化

分配的对象越多,GC性能就越差,所以需要减少对象分配的个数,比如对象复用,使用sync.Pool

注意:sync.Pool类似于缓存,其中的对象会被定期清理(GC时清理),不能放置像是数据库连接这样需要稳定存储的数据

参考

1.三色标记法是什么?
2.Go语言垃圾收集器原理

最后编辑于:2025-02-05 20:55:05


喜欢的朋友记得点赞、收藏、关注哦!!!

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

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

相关文章

DeepSeek教unity------MessagePack-03

数据契约兼容性 你可以使用 [DataContract] 注解代替 [MessagePackObject]。如果类型用 DataContract 进行注解,可以使用 [DataMember] 注解代替 [Key],并使用 [IgnoreDataMember] 代替 [IgnoreMember]。 然后,[DataMember(Order int)] 的…

【对比】Pandas 和 Polars 的区别

Pandas vs Polars 对比表 特性PandasPolars开发语言Python(Cython 实现核心部分)Rust(高性能系统编程语言)性能较慢,尤其在大数据集上(内存占用高,计算效率低)极快,利用…

百度千帆平台对接DeepSeek官方文档

目录 第一步:注册账号,开通千帆服务 第二步:创建应用,获取调用秘钥 第三步:调用模型,开启AI对话 方式一:通过API直接调用 方式二:使用SDK快速调用 方式三:在千帆大模…

49. c++计时器

为了测试某段特定代码的执行时间&#xff0c;体现代码的性能&#xff0c;可以使用计时器对代码段计时。下面使用std::chrono中的api编写简单案例&#xff1a; // // main.cpp // HelloWorld // // Created by on 2024/11/28. //#include <iostream> #include <vec…

Natural Language Processing NLP

NLP 清晰版本查看 Sentence segmentation (split)Tokenisation (split)Named entity recognition (combine) 概念主要內容典型方法Distributional Semantics&#xff08;分佈式語義&#xff09;&#xff08;分銷語義&#xff08;分佈式語義&#xff09;單詞的語義來自於它的…

Linux中线程创建,线程退出,线程接合

线程的简单了解 之前我们了解过 task_struct 是用于描述进程的核心数据结构。它包含了一个进程的所有重要信息&#xff0c;并且在进程的生命周期内保持更新。我们想要获取进程相关信息往往从这里得到。 在Linux中&#xff0c;线程的实现方式与进程类似&#xff0c;每个线程都…

HarmonyOS:使用List实现分组列表(包含粘性标题)

一、支持分组列表 在列表中支持数据的分组展示&#xff0c;可以使列表显示结构清晰&#xff0c;查找方便&#xff0c;从而提高使用效率。分组列表在实际应用中十分常见&#xff0c;如下图所示联系人列表。 联系人分组列表 在List组件中使用ListItemGroup对项目进行分组&#…

django上传文件

1、settings.py配置 # 静态文件配置 STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR /static, ]上传文件 # 定义一个视图函数&#xff0c;该函数接收一个 request 参数 from django.shortcuts import render # 必备引入 import json from django.views.decorators.http i…

【前端知识】浏览器兼容方案polyfill

浏览器兼容方案polyfill 什么是 Polyfill&#xff1f;Polyfill 的作用Polyfill 的工作原理1. **特性检测**2. **加载 Polyfill**3. **模拟实现** Polyfill 的常见场景Polyfill 的使用方式Polyfill 的优缺点优点缺点 常见的 Polyfill 库总结 什么是 Polyfill&#xff1f; Polyf…

C#学习之DateTime 类

目录 一、DateTime 类的常用方法和属性的汇总表格 二、常用方法程序示例 1. 获取当前本地时间 2. 获取当前 UTC 时间 3. 格式化日期和时间 4. 获取特定部分的时间 5. 获取时间戳 6. 获取时区信息 三、总结 一、DateTime 类的常用方法和属性的汇总表格 在 C# 中&#x…

dedecms 开放重定向漏洞(附脚本)(CVE-2024-57241)

免责申明: 本文所描述的漏洞及其复现步骤仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 0x0…

如何选择合适的超参数来训练Bert和TextCNN模型?

选择合适的超参数来训练Bert和TextCNN模型是一个复杂但关键的过程&#xff0c;它会显著影响模型的性能。以下是一些常见的超参数以及选择它们的方法&#xff1a; 1. 与数据处理相关的超参数 最大序列长度&#xff08;max_length&#xff09; 含义&#xff1a;指输入到Bert模…

AWS 前端自动化部署流程指南

本文详细介绍从前端代码开发到 AWS 自动化部署的完整流程。 一、流程概览 1.1 部署流程图 #mermaid-svg-nYg7k6L5IKVBjDtr {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-nYg7k6L5IKVBjDtr .error-icon{fill:#552…

Office word打开加载比较慢处理方法

1.添加safe参数 ,找到word启动项,右击word,选择属性 , 添加/safe , 应用并确定 2.取消加载项,点击文件,点击选项 ,点击加载项,点击转到,取消所有勾选,确定。

大数据SQL调优专题——Spark执行原理

引入 在深入MapReduce中有提到&#xff0c;MapReduce虽然通过“分而治之”的思想&#xff0c;解决了海量数据的计算处理问题&#xff0c;但性能还是不太理想&#xff0c;这体现在两个方面&#xff1a; 每个任务都有比较大的overhead&#xff0c;都需要预先把程序复制到各个 w…

MYSQL下载安装及使用

MYSQL官网下载地址&#xff1a;https://downloads.mysql.com/archives/community/ 也可以直接在服务器执行指令下载&#xff0c;但是下载速度比较慢。还是自己下载好拷贝过来比较快。 wget https://dev.mysql.com/get/Downloads/mysql-5.7.38-linux-glibc2.12-x86_64.tar.gz 1…

CentOS 7.8 安装MongoDB 7 副本集(Replica Set)

文章目录 1 环境假设步骤1&#xff1a;在两台服务器上安装MongoDB步骤2&#xff1a;配置副本集步骤3&#xff1a;初始化副本集步骤4&#xff1a;验证副本集配置步骤5&#xff1a;设置安全性&#xff08;可选&#xff09;扩展配置示例&#xff1a;最佳实践&#xff1a;仲裁节点步…

AJAX 与 ASP 的深入探讨

AJAX 与 ASP 的深入探讨 引言 随着互联网技术的飞速发展,Web应用程序的交互性和性能要求越来越高。AJAX(Asynchronous JavaScript and XML)和ASP(Active Server Pages)作为两种重要的Web开发技术,在提高Web应用程序性能和用户体验方面发挥着重要作用。本文将深入探讨AJ…

内网下,Ubuntu (24.10) 离线安装docker最新版教程

一般在数据比较敏感的情况下&#xff0c;是无法使用网络的&#xff0c;而对于Ubuntu系统来说&#xff0c;怎么离线安装docker呢&#xff1f; 下面我给大家来讲一下&#xff1a; 采用二进制安装&#xff1a; 1.下载docker离线包 官网下载&#xff1a; Index of linux/static…

Copilot Next Edit Suggestions(预览版)

作者&#xff1a;Brigit Murtaugh&#xff0c;Burke Holland 排版&#xff1a;Alan Wang 我们很高兴向你介绍在本次 Visual Studio Code 发布中&#xff0c;关于 GitHub Copilot 的三个预览功能&#xff1a; Next Edit Suggestions&#xff08;NES&#xff09;Copilot Edits 的…