不做大哥好多年 不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核

逍遥子

不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核
  • Python

  • GO

    • 01.GMP模型 ✅
    • 02.Map原理 ✅
    • 03.sync_map ✅
    • 04.sync.Pool
    • 05.垃圾回收 ✅
      • 01.设计原理
        • 1、标记-清除
        • 2、三色标记
        • 1)三色标记法介绍
        • 2)具体流程如下图
        • 3)STW 停止用户程序
        • 3、写屏障
        • 1)三色标记 误回收问题
        • 2)屏障技术
        • 3)混合写屏障
        • 4、垃圾回收触发机制
        • 5、避免全堆扫描
    • 06.channel
    • 07.内存逃逸
    • 08.内存泄漏
    • 09.mutex锁原理
    • 10.Go常识
    • 50.Golang常犯错误
    • 51.常见坑1~10
  • Java

  • 业务问题

  • 关键技术

  • 项目常识

  • 计算机基础

  • 常识
  • GO
xiaonaiqiang
2021-06-03
目录

05.垃圾回收 ✅

# 01.设计原理

  • 传统的系统级编程语言(主要指C/C++)中,开发人员需要自己申请释放内存
  • 后来新语言(java,python,php等等)都引入了语言层面的自动内存管理
  • 内存释放由虚拟机(virtual machine)或运行时(runtime)来自动进行管理
  • 而这种对不再使用的内存资源进行自动回收的行为就被称为垃圾回收

# 1、标记-清除

  • 它分为两个阶段:

    • 标记阶段 — 从根对象出发查找并标记堆中所有存活的对象
    • 清除阶段 — 遍历堆中的全部对象,回收未被标记的垃圾对象并将回收的内存加入空闲链表
  • 对象之间通过引用(指针)连在一起,构成一个有向图

  • 从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象

  • 清除阶段中收集器会依次遍历堆中的所有对象,释放其中没有被标记

  • 在上图中,可以从程序变量直接访问块1,并且可以间接访问块2和3,程序无法访问块4和5
  • 第一步将标记块1,并记住块2和3以供稍后处理
  • 第二步将标记块2,第三步将标记块3,但不记得块2,因为它已被标记
  • 扫描阶段将忽略块1,2和3,因为它们已被标记,但会回收块4和5

# 2、三色标记

# 1)三色标记法介绍

  • 三色标记法只是为了叙述方便而抽象出来的一种说法,实际上的对象是没有三色之分的
  • 这里的三色,对应了垃圾回收过程中对象的三种状态:
  • 1)灰色(可能指向其他白色):对象还在标记队列中等待
    • 已被回收器访问到的对象,不会被回收
    • 但回收器需要对其中的一个或多个指针进行扫描,因为他们可能还指向白色对象
  • 2)黑色(不指向其他白色):对象已被标记,gcmarkBits 对应位为 1 -- 该对象不会在本次 GC 中被回收
    • 已被回收器访问到的对象,其中所有字段都已被扫描
    • 黑色对象中任何一个指针都不可能直接指向白色对象
  • 3)白色(要被清除的):对象未被标记,gcmarkBits 对应位为 0 -- 该对象将会在本次 GC 中被清理

# 2)具体流程如下图

  • 在垃圾收集器开始工作时,程序中不存在任何的黑色对象,垃圾收集的根对象会被标记成灰色(栈和全局数据)
  • 标记清除主要包含下面四步
  • ① 把根对象标记为灰色(根是栈和全局数据)

  • ② 选一个灰色对象标记为黑色

  • ③ 将黑色对象所有指向的对象都标记成灰色

  • ④ 重复 2,3 步骤直到不存在灰色对象

# 3)STW 停止用户程序

  • 因为用户程序可能在标记执行的过程中修改对象的指针

  • 所以三色标记清除算法本身是不可以并发或者增量执行的,它仍然需要 STW

  • STW弊端
    • golang 的垃圾回收算法属于 标记-清除,是需要 STW 的
    • STW 就是 Stop The World 的意思,在 golang 中就是要停掉所有的 goroutine,专心进行垃圾回收,待垃圾回收结束后再恢复 goroutine
    • 而 STW 时间的长短直接影响了应用的执行,如果时间过长,那将是灾难性的
    • 其中写屏障和辅助 GC就是两种优化垃圾回收的方法
  • 1)写屏障(Write Barrier)
    • 而写屏障就是让 goroutine 与 GC 同时运行的手段,虽然不能完全消除 STW,但是可以大大减少 STW 的时间
    • 写屏障在 GC 的特定时间开启,开启后指针传递时会把指针标记,即本轮不回收,下次 GC 时再确定
  • 2)辅助 GC(Mutator Assist)
    • 为了防止内存分配过快,在 GC 执行过程中
    • GC 过程中 mutator 线程会并发运行,而 mutator assist 机制会协助 GC 做一部分的工作

# 3、写屏障

  • 写屏障的核心目的是确保垃圾回收器标记所有新创建或新引用的对象,避免漏标导致对象被错误回收
  • 三色标记法需要写屏障解决并发修改引用时可能导致的错误

# 1)三色标记 误回收问题

  • 标记过程需的要STW,因为对象引用关系如果在标记阶段做了修改,会影响标记结果的正确性
  • 例如下图(假设没有STW)
    • 1)灰色对象B引用白色对象C(此时C尚未被扫描)
    • 2)当遍历完A对象后,A变成黑色
      • 如果有其他程序断开了B对C的引用
      • 同时添加了A对C的引用(由于A是黑色,所以C会一直是白色,被回收)

# 2)屏障技术

1)强三色不变性:黑色不引用白色对象

  • 插入屏障:插入新引用时,如果被引用对象时白色,将其强制变为灰色
  • 缺点:实现复杂,开销较大,会对性能产生一定的影响

2)弱三色不变性:允许黑色对象暂时引用白色对象,但垃圾收集最终会修复这些状态

  • 插入屏障:插入新引用时,无需立刻标记为灰色,最终标记阶段前,必须修复黑白引用
  • 缺点:由于黑色对象允许指向白色对象,可能出现误回收问题
    • 如果 GC 在标记过程中漏掉了某个对象的引用路径
    • 可能会错误地将这个白色对象标记为不可达,从而在清理阶段进行误回收

# 3)混合写屏障

  • Go1.8版本引入的混合写屏障:工作机制
    • 混合写屏障原理:初期采用弱一致性,后期转为强一致性

为什么会出现混合写屏障?

  • 强三色不变性,要求黑色不引用白色对象,每次都要更新对象颜色,频繁调整状态,性能差
  • 弱三色不变性,允许黑色对象暂时引用白色对象,某些对象可能被误认为是垃圾
  • 混合写屏障的出现是为了解决并发垃圾回收中的性能与准确性之间的矛盾
  • 混合写屏障是一种折中方式,将强、弱三色一致性的特点结合
  • 在标记阶段的初期采用弱一致性,在标记阶段的后期转为强一致性
  • 初期可以快速标记对象,降低写屏障的频繁触发,而在后期进行严格检查,确保没有对象被漏标
  • 优点:平衡性能与准确性,减少写屏障开销
  • 缺点:实现复杂,调优难度大

为什么 弱三色不变性会出现误回收,但是 混合写屏障就不会 误回收?

  • 混合写屏障避免了误回收风险,因为它在标记阶段后期使用强一致性
  • 确保所有仍有可能被引用的对象被正确标记,从而避免了漏标和误回收

# 4、垃圾回收触发机制

  • 1、内存分配量达到阈值

    • 每次内存分配都会检查当前内存分配量是否达到阈值,如果达到阈值则触发 GC
    • 阈值 = 上次 GC 内存分配量 * 内存增长率
    • 内存增长率由环境变量 GOGC 控制,默认为 100,即每当内存扩大一倍时启动 GC
  • 2、定时触发 GC

    • 默认情况下,2 分钟触发一次 GC,该间隔由 src/runtime/proc.go 中的 forcegcperiod 声明
  • 3、手动触发 GC

    • 在代码中,可通过使用 runtime.GC() 手动触发 GC
  • GC 优化建议

    • 由上文可知,GC 性能是与对象数量有关的,对象越多 GC 性能越差,对程序的影响也越大

    • 所以在开发中要尽量减少对象分配个数,采用对象复用、将小对象组合成大对象或采用小数据类型(如使用 int8 代替 int)等

# 5、避免全堆扫描

Golang 主要依赖 三色标记清除 + 增量式 GC + 写屏障机制来 尽可能避免完整的全堆扫描

  • ① 三色标记清除 + 增量式 GC

    • 增量标记:GC 只标记存活对象,而非每次扫描整个堆
    • 三色法:白色(未扫描)、灰色(需扫描)、黑色(已扫描),减少重复扫描
  • ② 写屏障

    • 强三色不变性,黑色不引用白色对象
    • 弱三色不变性,允许黑色对象暂时引用白色对象
    • 混合写屏障原理:初期采用弱一致性,后期转为强一致性
  • ③ 栈上分配

    • 短生命周期对象分配在栈上,自动销毁,减少堆分配,减轻 GC 压力
  • ④ 对象池(sync.Pool)

    • 重用对象,避免频繁创建和销毁,减少 GC 负担
  • ⑤ 增量 GC

    • GC 分批处理对象,逐步标记,不会一次性回收所有垃圾,降低暂停时间
上次更新: 2025/4/29 17:38:19
04.sync.Pool
06.channel

← 04.sync.Pool 06.channel→

最近更新
01
04.数组双指针排序_子数组
03-25
02
08.动态规划
03-25
03
06.回溯算法
03-25
更多文章>
Theme by Vdoing | Copyright © 2019-2025 逍遥子 技术博客 京ICP备2021005373号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式