go的GC
1 Go语言的垃圾回收机制
Go语言的垃圾回收机制采用了基于标记和清除(mark-and-sweep)算法的垃圾回收器。它通过跟踪所有分配的对象并确定哪些对象不再被引用来实现垃圾回收。
以下是Go语言垃圾回收器的工作原理:
- 标记阶段(Marking phase):从根对象开始遍历整个对象图,将所有可达的对象标记为活动的。根对象包括全局变量、调用栈、寄存器和常量等。在标记期间,GC会停止所有Goroutine的执行。
- 清除阶段(Sweeping phase):遍历堆中的所有对象,将未被标记的对象清除掉,并将相应的内存返回给操作系统。在清除阶段,GC会恢复所有的Goroutine执行。
- 整理阶段(Compacting phase):将所有存活的对象向一端移动,使得堆空间变得连续。这个过程有助于减少内存碎片化,从而提高GC的效率。
整个过程是自动的,开发人员不需要手动地管理内存。Go语言的垃圾回收器通过并发的方式实现垃圾回收,因此在GC期间可以继续执行程序,这也是Go语言被广泛使用的原因之一。
2 三者标记法
- 白色对象:表示这些对象还没有被扫描,也就是还没有被标记。
- 灰色对象:表示这些对象已经被扫描,但是其引用的对象还没有被扫描。
- 黑色对象:表示这些对象已经被扫描,并且其引用的对象也已经被扫描。
扫描过程:
1、先将所有对象标记为白色
2、从根对象开始,根对象引用的对象标记为灰色
3、遍历灰色对象的引用对象,遍历到的对象标记为灰色,遍历完引用对象的灰色对象标记为黑色
4、不断执行3操作,最后直到灰色对象全部变成灰色
5、回收白色对象
3 stop the world
Golang中的STW(Stop The World)就是停掉所有的goroutine,专心做垃圾回收,待垃圾回收结束后再恢复goroutine。
STW时间的长短直接影响了应用的执行,时间过长对于一些web应用来说是不可接受的,这也是广受诟病的原因之一。
4 垃圾回收优化
写屏障:
-
写屏障是指在进行堆上的写操作时,会插入一些额外的代码来协助垃圾回收器识别哪些指针被更新了。当写入指针时,写屏障会将指针标记为"灰色",表示垃圾回收器需要检查这个指针指向的对象是否需要被保留。这样,垃圾回收器就可以跟踪所有指针的变化,从而保证不会误判哪些对象仍在使用中。
-
写屏障可以保证垃圾回收器能够正确地识别哪些对象是可达的,从而避免了内存泄漏和垃圾对象的产生。同时,写屏障也会带来一些额外的开销,因为它需要在每次写入指针时进行一些额外的操作。在实践中,写屏障的性能影响通常是可以接受的,因为它能够大大提高垃圾回收的准确性和可靠性。
-
GC过程中新分配的内存会被立即标记,用的并不是写屏障技术,也即GC过程中分配的内存不会在本轮GC中回收。
辅助GC(Mutator Assist):
- Mutator Assist机制是在垃圾回收器扫描堆时,将堆的一部分交给Mutator线程进行标记。这样,Mutator线程就可以帮助垃圾回收器标记一些对象,并且避免了垃圾回收器对整个堆的扫描,从而提高了垃圾回收的效率。
- 在Go语言中,Mutator Assist是一种可选的GC模式。如果开启了Mutator Assist,Go语言的垃圾回收器将会定期向Mutator线程发送指令,要求它们协助标记对象。这样,就能够更快地进行垃圾回收,同时也避免了Mutator线程长时间的停顿,提高了程序的响应性。
5 垃圾回收的触发
阈值触发:
阀值 = 上次GC内存分配量 * 内存增长率
内存增长率由环境变量GOGC
控制,默认为100,即每当内存扩大一倍时启动GC。
定时触发:
默认情况下,最长2分钟触发一次GC,这个间隔在src/runtime/proc.go:forcegcperiod
变量中被声明
手动触发:
也可以使用runtime.GC()
来手动触发GC
6 其他语言的垃圾回收机制:
- Python、PHP、Swift 引用计数
- java 分代收集