GC看这一篇就够了


感谢幼麟实验室的生动讲解
垃圾回收之写屏障
go GC探秘

引言

由于函数调用栈会在函数返回后销毁,如果不能在编译阶段确定数据对象的大小或者对象生命周期超出当前所在函数,那就不适合分配在栈上 而应该分配到堆上
他们占用的内存需要程序主动释放才可以重新使用 否则成为垃圾
在这里插入图片描述

非追踪

引用计数  及时回收  开销大

追踪式

  • 标记-清除,目前主流的垃圾回收算法:核心思想是可达性近似等价于存活性的

  • 分代 基于弱分代回收假说 大部分对象都在年轻时死亡

单核

前提:暂停用户程序,只专注于垃圾回收

  • 自动垃圾回收:

由运行时识别不再有用得数据并释放他们占用的内存
内存何时被释放 被释放的内存如何处理
那么,问题来了,自动垃圾回收怎么区分哪些数据对象是垃圾呢?
还得从虚拟地址空间看,程序中用的到的数据一定是从栈、数据段这些根节点追踪得到的数据
虽然能够追踪的到 不代表一定用到,但是从这些根节点追踪不到的数据 就一定不会被用到
在这里插入图片描述

  • 三色抽象 (标记-清扫的一种)

可以清晰的展示 追踪式回收过程中对象状态的变化过程
还可以推演回收器的正确性
1、垃圾回收开始时所有对象都为白色
2、把直接追踪到的root节点都标记为灰色 灰色代表基于当前节点展开的追踪还未完成
3、当基于某个节点的追踪任务完成后 便会把该节点标记为黑色 表示它是存活节点 也无须重复标记
在这里插入图片描述

  • 标记-清扫算法带来的问题:造成内存碎片化,要找到合适的内存代价高 也可能找不到

1、可以基于BiBOP思想 把内存块划分成多种规格的 对相同规格的内存块进行统一管理 这样可以更快的匹配到大小合适的空闲内存
2、也可以移动数据 多次扫描和移动数据开销不容忽视
3、复制式回收 只有一半的堆内存可用
在这里插入图片描述

  • 尽可能的缩短STW的时间,引出增量式垃圾回收

实际上,我们总是希望能够缩短暂停的时间,可以将垃圾回收工作分多次完成,也就是用户程序和垃圾回收交替执行

  • 带来的问题:mark工作的一致性问题

下面这两个任何一个都可以解决问题
强三色不变式 不允许出现黑色到白色的引用 插入写屏障
若三色不变式 允许黑色到白色的引用,但有灰色对象能够到达该白色对象 删除写屏障

  • 怎么实现呢?

建立读写屏障
写屏障会在写操作中插入指令,目的时把数据对象的修改通知垃圾回收器,所以写屏障通常都会有一个记录集
具体实现要考虑的问题:(顺序存储 哈希表 记录精确到被修改的对象 记录其所在页)
非移动式垃圾回收中,天然的不需要读屏障
在这里插入图片描述

多核

  • 多核:暂停用户程序的前提下,多线程 并行垃圾回收

并行场景下 同步时不可避免规避的问题
并发垃圾回收时用户程序和回收器同时执行
如果没有STW,那么垃圾回收开始的消息便很难准确及时地通知所有线程
实际应用中,某些阶段实行STW, 主体并发式 主体并发增量式
在这里插入图片描述

Go的GC

  • go:mark-swap算法支持主体并发增量式回收
  • GC在准备阶段

会为每个P创建一个mark worker协程
把对应的g指针存储到P中,这些后台mark worker创建后很快陷入休眠 等待得到调度

  • 第一次STW

开启写屏障 等所有准备工作做好以后 所有P都会知道写屏障已经开启

  • GC进入mark阶段

全局变量gcphase记录GC阶段标识 _GCMark
全局变量writeBarrier记录是否开启写屏障 true
全局变量gcBlancenEnabled 用于标记是否允许进行GC标记工作 1
然后,后台这么mark worker 可以得到调度执行 展开标记工作
当没有标记工作时,

  • 第二次STW
  • GC进入MarkTermination阶段

确认所有标记工作已经完成
然后停止标记工作 将 gcphase=_GCMarkTermination gcBlancenEnabled =0

  • GC进入OFF阶段

关闭写屏障 gcphase=_GCOFF writeBarrierEnabled=false
进入OFF阶段之前,新分配的对象会直接标记为黑色,
进入OFF阶段之后,再新分配的对象会标记为白色

  • 进入清扫阶段

执行清扫工作的协程由runtime再gcenable中创建 对应g指针存储在全局变量sweep中
这个sweeper 会被加入到run queue中,它得到调度执行时,会执行清扫任务 清扫工作也是增量进行的
所以,每一轮GC开始之前 还要确保上一轮GC未完成的清扫工作
在这里插入图片描述

QA

Q1:哪些指针是GC感兴趣的?

在这里插入图片描述

  • go的内存管理

8K为一页page
多个页组成一个span
每个span都只存储一种大小的元素
大小 覆盖了小于等于32K的66种
大于32K的大对象直接在mheap中分配
多个span组成一个area
在这里插入图片描述

  • 如果协程都向mheap同时申请内存,那么同步开销太大,引入全局与本地span缓存

基于heaparea记录的元数据信息,我们只要知道一个对象的地址
1、根据bitmap信息扫描它内部是否含有指针
2、也可以根据对象地址,计算出它在哪一页

在这里插入图片描述

每个span都对应两个位图标记
在这里插入图片描述

Q2工作队列

通过区分本地工作队列与全局工作缓存,并为每个P设置写屏障缓冲区,缓解了执行并发标记工作时操作工作队列的竞争问题
在这里插入图片描述

Q3:CPU Utilization

在这里插入图片描述

Q4:Memory alloc

在这里插入图片描述

Q5:Hybrid Barrier

在这里插入图片描述

Q6:CPU Trigger

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值