go语言垃圾回收

Go语言——垃圾回收

屏障: 1.插入屏障:在A对象引用B对象的时候,B对象被标记为灰色(满足强三色不变式,黑色引用的白色对象会被强制转换为灰色)。只有堆上的对象触发插入屏障,栈上的对象不触发插入屏障。在准备回收白色前,重新遍历扫描一次栈空间。此时加STW暂停保护栈,防止外界干扰。

不足:结束时需要使用STW来重新扫描栈

2.删除屏障:被删除的对象,如果自身为灰色或者白色,那么被标记为灰色(满足弱三色不变式)。

删除屏障的不足:回收精度低,一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉。

Go V1.8的三色标记法+混合写屏障机制 具体操作: 1.GC开始将栈上的可达对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW) 2.GC期间,任何在栈上创建的新对象,均为黑色 3.堆上被删除对象标记为灰色 4.堆上被添加的对象标记为灰色 满足:变形的弱三色不变式(结合了插入、删除写屏障的优点)

对于堆上的对象,采用三色标记法+写屏障保护

GC垃圾收集的多个阶段:

1.标记准备阶段;

启动后台标记任务 暂停程序(STW),所有的处理器在这时会进入安全点(Safe point); 如果当前垃圾收集循环是强制触发的,我们还需要处理还未被清理的内存管理单元; 将根对象入队 开启写屏障 2.标记阶段;

恢复用户协程 使用三色标记法开始标记,此时用户协程和标记协程并发执行 3.标记终止阶段;

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

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

什么是根对象?

根对象(root object)是指那些能够从全局可达的地方访问到的对象。垃圾回收器会从根对象开始,通过遍历根对象的引用关系,逐步追踪并标记所有可达的对象。任何未被标记的对象都会被认为是垃圾,最终被回收释放。

1.全局变量:全局变量可以被程序中的任何位置引用到,因此是根对象。 2.当前正在执行的函数的局部变量:当一个函数正在执行时,其局部变量可以被当前函数中的代码访问到,因此也是根对象。 3.当前正在执行的 goroutine 的栈中的变量:goroutine 是 Go语言并发编程中的轻量级线程,每个 goroutine 都有一块独立的栈空间,其中的变量可以被当前 goroutine 访问到,也是根对象。 4.其他和运行时系统相关的数据结构和变量。

三色标记法的缺点:

1.暂停时间:在进行垃圾回收时,必须停止程序执行,这会导致应用程序暂停。引入写屏障保护可以减少暂停时间, 但仍然可能导致性能下降。 2.内存开销:三色标记法需要为每个对象维护额外的状态信息,以记录其标记状态。这会增加内存开销,并可能对内 存资源造成负担。 3.频繁的垃圾回收:三色标记法需要频繁地迭代标记和清除对象,如果要回收的垃圾对象很多,可能会导致回收过程 变得非常耗时。 4.碎片化:垃圾回收过程中,如果频繁进行对象的移动和重新分配内存,可能会导致内存碎片化,降低内存的利用 率。

GC的触发条件

1.主动触发(手动触发),通过调用 runtime.GC 来触发GC,此调用阻塞式地等待当前GC运行完毕。 2.被动触发,分为两种方式:

2.1.使用步调(Pacing)算法,其核心思想是控制内存增长的比例,每次内存分配时检查当前内存分配量是否已达到阈值(环境变量GOGC):默认100%,即当内存扩大一倍时启用GC。 2.2.使用系统监控,当超过两分钟没有产生任何GC时,强制触发 GC。

GC调优

1.控制内存分配的速度,限制Goroutine的数量,提高赋值器mutator的CPU利用率(降低GC的CPU利用率) 2.少量使用+连接string 3.slice提前分配足够的内存来降低扩容带来的拷贝 4.避免map key对象过多,导致扫描时间增加 5.变量复用,减少对象分配,例如使用sync.Pool来复用需要频繁创建临时对象、使用全局变量等 6.增大GOGC的值,降低GC的运行频率

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计