1、常见的GC实现方式有哪些?
常用的GC回收机制主要分追踪法、引用计数法这两大类。
引用计数法 ,就是每次被引用就加一,被取消引用就减一,当为零时,就可以清除掉。
Python就主要用的是引用计数法,优点是快,且方便。但缺点是,两个指针相互引用时,就会发生内存泄露。
而追踪法是从根Root节点开始,遍历所有存活的对象,没被遍历到的就是可以清除掉的。而 GO 语言使用的就是标记追踪法。
2、Go语言的 GC使用的是什么?
Go 语言目前使用的就是,能够并发执行,的三色标记算法。
也就是能够从根开始遍历的标记-清除算法。
3、三色标记法是什么?
三色标记法是GC垃圾回收的核心算法。通过3种颜色,代表三种状态。
白色 :未被访问过的对象,标记阶段结束后,会被删除。
灰色 :已经被访问,但其引用的对象还没有被访问,会将该对象置为灰色。是待处理队列。
黑色 :已经被访问,且直接引用的对象也被访问过,则将其置为黑色。是确定存活队列。
标记流程:
开始的时,所有对象都是白色。先取出所有根Root对象,然后将其标记灰色,放到等待队列中。
之后,就从等待队列中取出一个灰色对象,若其引用的对象是白色,就置灰并放到等待队列中。直到其所有引用的对象全部被遍历过,然后将灰色对象变为黑色,放到以确定存活队列中。最后在将所有白色对象给清除掉。
4、Go 语言GC的根对象到底是什么?
GC的根对象,在GC中的术语叫做根集合。它是GC开始时,被扫描的第一批对象。
全局变量 :在程序编译期间,就已经被确定会在程序的整个生命周期存活的对象。
执行栈 :每个goroutine上都有一个执行栈,所有活跃的协程上的指针类型变量也会被视为根对象(map、slice...)。
寄存器:里面也存的值也能是指针,若其指向堆内对象,也会被视为根对象。
5、STW是什么?
STW,就是stop the world,它通常代表用户业务函数的停顿。一般会发生在 标记开始前的获取根对象时 与 开始标记结束后的收尾阶段。
6、并发标记清除的难点是什么?
难点就是,GC边标记对象,业务函数边执行时,造成GC标记错误,导致误删除了一些对象。
一般是发生在弱三色不变性的情况下。
7、Go语言是如何解决并发标记清除时,用户程序并发修改对象引用问题的?
Go语言通过 混合写屏障 配合 弱三色不变性 两者结合来实现的。
因为原本面对的问题 就是:
黑色对象,直接引用了白色对象。而白色对象与其被引用的灰色对象之间的连接断掉了。最终导致该白色对象可能会被误删。
混合写屏障解决的就是,堆内的对象被引用时,会将引用的对象所引用的旧对象保护起来置灰。然后新引用的对象也给保护起来置灰。
在标记结束前的STW阶段,再拿出来处理。从而避免了并发丢失白色对象问题。
8、什么是写屏障、混合写屏障如何实现?
写屏障就是编译器在执行指针操作时,会插入一段额外的逻辑,将被引用的对象保护起来。
说到混合写屏障,需要先分别知道增加写屏障与删除写屏障。
其中 增加写屏障 是对新增加的白色对象置为灰色,但它不官删除的对象。
而 删除写屏障 是对删除的对象置为灰色,但是它不官新增的对象。
而 混合写屏障 ,借鉴了两者的优点,对指针做出修改时,同时覆盖了新增 与删除两种场景。Go1.8也因为混合写屏障的优点,可以不用重新遍历栈,大大的降低了STW时间。
9、Go语言中 GC 的流程:
在标记前,先进行一次STW,获取所有的根对象集合。之后进行三色标记算法,同时并发执行业务函数,当标记阶段收尾时,再次启动一次很短的STW处理,被额外标记的堆对象,之后边执行业务函数,边并发清除垃圾对象。
10、GC 触发的时机有哪些?
主动触发:
- 通过调用runtime.GC(),进行触发。
被动触发:
- Go后台会有监控线程,一旦检测到堆内存是否触发了GC的阈值,来判断是否需要进行GC。
- 第一次GC时,一旦堆内存达到4M就会自动触发。
- 如果长达两分钟内未触发GC,那就自动触发。
11、GC 关注的指标有哪些?
- CPU占比:CPU利用率一般要控制在5%以内。
- GC停顿时间:一般由STW决定。(标记准备阶段,标记结束阶段)
- GC停顿频率:看GC的次数,一般可以由GOGC与GOmemLimit控制。
12、有了 GC 为什么还会发生内存泄露?
内存泄露是区别内存逃逸的。所以需要先明白什么时内存泄露:
本来已经没用的内存,却还被引用着,导致GC无法清除。
- 本来该释放的内存,却依旧被根对象引用。比如堆上没用的指针,却被全局map引用。
- goroutine泄露,导致无法回收。
内存泄露是区别与内存逃逸的,内存逃逸是本来变量的生命周期是栈的,却意外跑到了堆上。
13、Go 的 GC如何调优?
1、可以减少的内存的分别,比如引入sync.Pool进行优化。
2、合理调节GOGC与GOMEMLimit这两个字段。
14、如何观察 Go GC?
powershell:
开始 $env:godebug:"gotrace=1"
go run main.go
取消 :remove-item env:godebug
powershell
gc 1 @0.012s 1%: 0+1.0+0 ms clock, 0+0.52/2.6/0+0 ms cpu, 4->4->2 MB, 4 MB goal,
0 MB stacks, 1 MB globals, 16 P
他们的含义分别是:
1、gc 1 第一个gc阶段
2、开始于程序启动0.012秒后
3、cpu利用率为1%
4、标记准备阶段0ms
5、并发标记阶段1.0ms
6、标记结束前的收尾阶段用了0ms
7、紧接着就是cpu相关的。这里不做解释。
8、4是标记开始前4MB堆内存。
9、4是标记结束时还是4MB内存。
10、清理后是2MB。
11、最第目标是剩余4MB。
当然,还可以通过pprof生产可视化界面进行操作。