0%

go语言基础3——GC相关

什么是GC

垃圾回收就是对程序中不再使用的内存资源进行自动回收的操作。

内存上分配的数据对象,不会再使用时,不会自动释放内存,就变成垃圾,在程序的运行过程中,如果不能及时清理,会导致越来越多的内存空间被浪费,导致系统性能下降。

因此需要内存回收,内存回收分为两种方式:

  1. 手动释放占用内存空间

    可能会出现的问题:
    悬挂指针: 释放的早了,后续对数据的访问就会出错,因为对应的内存空间可能已经清空,重新分配,甚至是归还给操作系统了。
    内存泄漏: 如果忘了释放,一直占用内存,导致内存泄漏。

  2. 自动内存回收

    程序自动检测对象决定是否要回收其内存。

    核心思想:程序中用得到的数据,一定是可以从栈或数据段这些根节点追踪得到的数据,追踪不到的数据,肯定用不到,也就是垃圾。

go gc 是怎么实现的?

一次完整的垃圾回收会分为四个阶段,分别是标记准备、标记开始、标记终止、清理:

  1. 标记准备(Mark Setup):打开写屏障(Write Barrier),需 STW(stop the world)
  2. 标记开始(Marking):使用三色标记法并发标记 ,与用户程序并发执行
  3. 标记终止(Mark Termination):对触发写屏障的对象进行重新扫描标记,关闭写屏障(Write Barrier),需 STW(stop the world)
  4. 清理(Sweeping):将需要回收的内存归还到堆中,将过多的内存归还给操作系统,与用户程序并发执行

  1. GC机制随着golang版本变化如何变化的?

    go 1.3 之前采用标记清除法,需要STW
    go 1.5 采用三色标记法,插入写屏障机制(只在堆内存中生效),最后仍需对栈内存进行STW
    go 1.8 采用混合写屏障机制**,屏障限制只在堆内存中生效。避免了最后节点对栈进行STW的问题,提升了GC效率

  2. 一个概念:STW:stop the word,指程序执行过程中,中断暂停程序逻辑,专门去进行垃圾回收。(STW目的是为了防止GC扫描时内存变化引起的混乱

  3. 标记清除法

    从根变量开始遍历所有引用的对象,标记引用的对象,没有被标记的进行回收。

    • 开启STW,
    • 从根节点出发,标记所有可达对象
    • 停止STW,然后回收所有未标记的对象。
    • 优点:解决了引用计数的缺点。
    • 缺点:需要 STW,暂时停掉程序运行。
  4. 三色标记法【改进的标记清除法】+写屏障机制的流程

三色标记法是对标记阶段的改进,原理如下:

  • 初始状态所有对象都是白色。
  • 从root根出发扫描所有根对象(下图a,b),将他们引用的对象标记为灰色(图中A,B)

那么什么是root呢? 看了很多文章都没解释这这个概念,在这儿说明下:root区域主要是程序运行到当前时刻的栈、寄存器和全局变量。

image-20220802113108020

  • 遍历灰色对象,将灰色对象引用的对象由白色 也变成灰色对象,然后将遍历过的灰色对象变成黑色对象。

  • 循环步骤3,直到灰色对象全部变黑色。重复以上操作

    其中通过写屏障(write-barrier)【写屏障就是让goroutine与GC同时运行的手段,大大减少STW的时间,开启后指针传递时会把指针标记,本轮不回收,下轮GC时回收】检测对象有变化;还有一个辅助GC(mutator assist)机制【为防止内存分配过快,GC过程中,辅助GC线程并发运行,协助GC做一部分工作,先做一部分标记工作】,

  • 收集所有白色对象(垃圾)。

插入写屏障

对象被引用时触发的机制(只在堆内存中生效):赋值器这一行为通知给并发执行的回收器,被引用的对象标记为灰色

缺点:结束时需要STW来重新扫描栈,标记栈上引用的白色对象的存活

删除写屏障

对象被删除时触发的机制(只在堆内存中生效):赋值器将这一行为通知给并发执行的回收器,被删除的对象,如果自身为灰色或者白色,那么标记为灰色

缺点:一个对象的引用被删除后,即使没有其他存活的对象引用它,它仍然会活到下一轮,会产生很大冗余扫描成本,且降低了回收精度

混合写屏障

GC没有混合写屏障前,一直是插入写屏障;混合写屏障是插入写屏障 + 删除写屏障,写屏障只应用在堆上应用,栈上不启用(栈上启用成本很高)

  • GC开始将栈上的对象全部扫描并标记为黑色。
  • GC期间,任何在栈上创建的新对象,均为黑色。
  • 被删除的对象标记为灰色。
  • 被添加的对象标记为灰色。
GC 的触发时机

主动触发

  • 调用 runtime.GC() 方法,触发 GC

被动触发:

  • 定时触发,该触发条件由 runtime.forcegcperiod 变量控制,默认为 2 分 钟。当超过两分钟没有产生任何 GC 时,触发 GC
  • 根据内存分配阈值触发,该触发条件由环境变量GOGC控制,默认值为100(100%),当前堆内存占用是上次GC结束后占用内存的2倍时,触发GC
-------------本文结束感谢您的阅读-------------
打赏一瓶矿泉水