GO语言垃圾回收

垃圾分为两种垃圾:

语义垃圾:有些场景也被称为内存泄漏,语义垃圾指的是从语法上可达(可以通过局部、全局变量被引
用)的对象,但从语义上来将他们是垃圾,垃圾回收器对此无能为力;
语法垃圾:从语法上无法到达的对象,这个是垃圾回收器的目标;
垃圾回收是一种自动管理内存的机制,垃圾回收器会去尝试回收程序不再使用的对象已经占用的内存;
像C语言需要手动管理内存,管错或者管漏内存也很糟糕,会直接导致程序不稳定(持续泄漏)甚至崩
溃;
所以现在编程语言大多都有GC,Go语言也实现了GC;

Go语言GC的触发场景分为两大类:分别是:

1. 系统触发,运行时根据内置的条件,检查、发现到,则进行GC处理,维护整个应用程序的可用
性;系统触发的三个条件:
	1. gcTriggerHeap:当所分配的堆大小达到阈值(由控制器计算的触发堆的大小)时,将会触
	发;
	2. gcTriggerTime:当距离上一个GC周期的时间超过一定时间时,将会触发。时间周期以
	runtime.forcegcperiod变量为准,默认2分钟
	3. gcTriggerCycle:如果没有开启GC,则启动GC
2. 手动触发:开发者在业务代码中自行调用runtime.GC方法来触发GC行为

GC基本流程:

在这里插入图片描述

1. 在开始新的一轮GC周期前,需要调用gcWaitOnMark方法上一轮GC的标记结束(含扫描中止、标
记、或标记终止等)
2. 开始新的一轮GC周期,调用gStart方法触发GC行为,开始扫描标记阶段
3. 需要调用gcWaitOnMark方法等待,直到当前GC周期的扫描、标记、标记终止完成;
4. 需要调用sweepone方法,扫描未切除的堆跨度,并持续扫除,保证清理完成,保证清理完成,在
等待扫除完毕前的阻塞时间,会调用Gosched让出
5. 在本轮 GC 已经基本完成后,会调用 mProf_PostSweep 方法。以此记录最后一次标记终止时的堆
配置文件快照。
6. 结束,释放M

GC在哪触发

监控线程

实质上在Go运行时(runtime)初始化时,会启动一个goroutine,用于处理GC机制的相关事项;处理
完毕后,会调用goparkunlock方法让该goroutine陷入休眠等待状态,以减少不必要的资源开源;在休
眠的时候会由sysmon这一个系统监控线程来进行监控、唤醒等行为;sysmon中不断的进行for循环判
断 gcTriggerTime 和 now 变量进行比较,判断是否达到一定的时间(默认为 2 分钟)。
若达到意味着满足条件,会将 forcegc.g 放到全局队列中接受新的一轮调度,再进行对上面
forcegchelper 的唤醒。
堆内存申请
在了解定时触发的机制后,另外一个场景就是分配的堆空间的时候,运行时申请堆内存的mallocgc方
法:
小对象:如果申请小对象时,发现当前内存空间不存在空闲跨度时,就会需要调用nextFree方法获取新
的可用对象,可能会触发GC行为
大对象:如果申请大于32K以上的大对象时,可能会触发GC行为;

Go语言的垃圾回收

在1.3 之前主要使用标记清除算法,分为两个步骤:
	1. 暂停程序业务逻辑,找出不可达的对象,然后做标记;
	2. 清除未被标记的对象
	3. 程序暂停取消,周期重复该操作
在1.5以后开始引入三色标记法,这里的三色,对应了垃圾回收过程中对象的三种状态:
灰色:对象还在标记队列中等待
黑色:对象已经被标记,该对象不会再本次GC中被清理
白色:对象未被标记,该对象将会在本次GC中被清理
	1. 初始状态所有对象都是白色
	2. 从根节点开始遍历所有对象,把遍历到的对象变成灰色对象
	3. 遍历灰色对象,将灰色对象引用的对象也变成灰色对象,然后将遍历过的灰色对象变成黑色对象
	4. 循环步骤三,直到灰色对象全部变成黑色
	5. 通过写屏障检测对象是否有变化,重复以上操作(因为mark和用户程序是并行的,所以在上一步
	执行的时候可能会有新的对象分配,写屏障是为了解决这个问题引入的)
	6. 收集所有白色对象

对象丢失问题

GC线程/协程与应用线程/协程是并发执行的,在GC标记worker工作期间,应用还会不断地修改堆上对
象引用的关系,所以需要解决漏标、错标的问题,我们先定义"三色不变性",如果我们堆上对象的引用
关系不管怎么修改,都能满足三色不变性,那么也不会发生对象丢失问题;
强三色不变性就是禁止黑色对象指向白色对象;
弱三色不变性就是黑色对象可以指向白色对象,但指向的白色对象,必须有能从灰色对象可达的路径;

无论应用于GC并发执行期间如何修改堆上对象的关系,只要修改之后,堆上对象能满足任意一种不变
性,就不会发生对象丢失的问题;
实现强/弱三色不变性均需要引入屏障技术,Go语言使用写屏障;
Go语言只有write barrier,没有read barrier,在应用进入GC标记阶段前的STW阶段,会将全局变量
runtime.writeBarrier.enabled修改为true,这时堆上指针修改操作在修改之前都会调用
gcWritebarrier;

常见的write barrier有两种:

Dijistra Insertion Barrier:指针修改时,指向的新对象要标灰(插入屏障)
Yuasa Deletion Barrier:指针修改时,修改前指向的对象要标灰(删除屏障)

Go语言混合了两种屏障,官方选择了更为简单的实现,即指针断开的老对象和新对象都标灰的实现;

写屏障优化

STW的目的是防止GC扫描时内存变化而停掉goroutine,而写屏障就是让goroutine与GC同时运行的手
段,虽然写屏障不能完全消除STW,但是可以大大减少STW的时间,写屏障类似一种开关,在GC的特定
时机开启,开启后指针传递时会把指针标记,即本轮不回收,下次 GC 时再确定。GC 过程中新分配的内
存会被立即标记,用的并不是写屏障技术,也即GC过程中分配的内存不会在本轮GC中回收。

辅助GC

为了防止内存分配过快,在 GC 执行过程中,如果 goroutine 需要分配内存,那么这个 goroutine 会参
与一部分GC的工作,即帮助 GC 做一部分工作,这个机制叫作 Mutator Assist。

回收流程

相比复杂的标记流程,对象的回收和内存释放进程启动时会有两个特殊goroutine:
一个叫sweep.g,主要负责清扫死对象,合并相关的空闲页
一个叫scvg.g,主要负责向操作系统归还内存;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值