Go教程

Golang 垃圾回收

本文主要是介绍Golang 垃圾回收,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Go 垃圾回收使用的是标记-回收算法,分为四个阶段:标记准备阶段,标记阶段,标记终止阶段,清理阶段。

一、垃圾回收流程

image

1)标记准备阶段

进行的操作:

  • 判断当前协程是否可以抢占,不可以抢占不触发GC;
  • 启动后台标记协程;
  • 暂停用户协程(STW - stop the word)
  • 开启写屏障
  • 将根对象入队

标记协程的启动数量?
每个逻辑处理器P会启动一个标记协程,但并不是每个标记协程都会执行,同时执行的标记协程数量大约是P的25%,也就是有25%的CPU用在GC上。例如,有4个P,那同一时间只有1个标记协程在执行。这样做可以减少GC给用户程序带来的影响。
[代码:runtime/mgc.go.startCycle()]

标记协程的调度过程?
STW后,重新启动所有协程,调度器会进行新的一轮调度,这时会判断程序是否处于GC阶段,如果是则判断是否需要执行标记任务。怎么判断呢?分两种情况:
第一种,需要执行的标记协程数(dedicatedMarkWorkersNeeded)大于0时,此时是DedicatedMode模式;
第二种,fractionalUtilizationGoal大于0且当前P执行标记任务的时间小于fractionalUtilizationGoal*当前标记周期的时间,这种情况只会占用部分时间来进行标记任务,超出时间后,其它协程可以抢占,此时是FractionalMode模式。
[代码:runtime/mgc.go.findRunnableGCWorker()]

为什么要开启写屏障?
因为用户协程和标记协程是并发执行的,在标记的过程中,函数栈内可能会有新分配的对象,如果没有开启写屏障,那新分配的对象是标记为白色的,可能会在这次GC中被误回收。写屏障的作用就是把新分配的对象标记为灰色,避免误回收,这些灰色对象会在下次GC中判断是否需要回收。

根对象包含什么?
根对象包含了全局对象、栈对象(goroutine栈)

2)标记阶段

进行的操作:

  • 恢复用户协程;
  • 使用三色标记法开始标记,此时用户协程和标记协程并发执行;

标记任务的模式有哪些?
1、DedicatedMode:代表当前P专门负责标记对象,不会被抢占;
2、FractionalMode:在标记任务到达目标时间后,会自动退出;
3、IdleMode:当前P没有可以执行的G,执行标记任务,直到被抢占;

在DedicateMode模式中,协程不能被抢占,那这个P中的G将得不到执行,怎么办呢?
先执行可以被抢占的标记协程,如果标记协程已经被抢占了,则将当前P中的G挪到全局队列中,进入不能被抢占的模式

三色标记法:

黑色对象:对象已被标记,包含的子对象也被标记,不会在本次GC中回收
灰色对象:对象已被标记,但包含的子对象尚未标记,不会在本次GC中回收
白色对象:对象未标记,会在本次GC中回收

image

三色标记法用是否可达来判断对象是否存活,首先会扫描根对象,将根对象引用的对象标记为灰色;然后处理灰色对象,将灰色对象引用的对象标记为灰色,自身标记为黑色,循环处理灰色对象,直到灰色对象集合为空,此时只剩黑色对象和白色对象

3)标记终止阶段

进行的操作:

  • 暂停用户协程
  • 计算下一次触发GC时需要达到的堆目标
  • 唤醒后台清扫协程

4)清理阶段

进行的操作:

  • 关闭写屏障
  • 恢复用户协程
  • 异步清理回收

二、垃圾回收触发时机

  1. 内存分配量达到阈值:每次内存分配都会判断当前内存是否达到阈值,如果是则触发GC。阈值为当前堆内存达到2倍上一次GC后的内存,2倍为内存增长率,可通过环节变量GOGC调整;
  2. 定时触发:默认2分钟触发一次,这个配置在runtime/proc.go里的forcegcperiod参数;
  3. 手动触发:使用runtime.GC() 手动触发;

浅析 Golang 垃圾回收机制
深入理解Go-垃圾回收机制
《Go 语言底层原理剖析》 - 郑建勋

这篇关于Golang 垃圾回收的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!