浅谈Golang垃圾回收

众所周知,Go 是一门自带垃圾回收的语言,那么 Go 的 GC 是怎么实现的,和其他的自带垃圾回收的语言又有什么区别呢?在弄清楚怎样 GC 之前最好能清楚程序是怎样运行的,在 Go 里面,我们可以轻松地创建数以万计的协程,他们被调度器调度到系统线程上去运行,此外因为程序是提前被编译好的,一个二进制文件就包含了所有的内容,可以在任何环境去运行,但是因为没有 JIT,我们不能做一些相关的优化。

对于 C++ 这样的语言,程序员可以手动申请和释放内存,这给了程序极大的便利,但是相应的也提高了要求,如果忘了释放内存或者没有正确地释放内存,那么就有可能造成内存泄露,而 Go,Python 等语言就是就是自己干了这件事,但是每个语言的实现方式也是不太相同的,大体有以下几种。

引用计数:引用计数会为每个对象维护一个计数器,当该对象被其他对象引用时加一,引用失效时减一,当引用次数归零时就被回收,这种方式原理和实现都比较简单,而且回收的及时性很好,也不用暂停程序就可以回收,但是它无法解决循环引用问题,而且时间和空间成本都比较高,因为需要记录和计算引用次数。

标记-清除:记录需要回收的垃圾对象,在标记完成后回收垃圾对象的内存空间。

三色标记法:初始时所有对象都是白色对象,从GC Root对象出发,扫描所有可达对象并标记为灰色,放入待处理队列,从队列取出一个灰色对象并标记为黑色,将其引用对象标记为灰色放入队列,重复上一步骤,直到灰色对象队列为空,此时所有剩下的白色对象就是垃圾对象。

但是三色标记法在执行过程中也可能会遇到问题,假设在某个节点被标记为黑色节点以后它又引用了某个白色节点,但是最后这个白色节点又被清除,那么黑色节点指向一个不存在的地址就会引发严重的问题,一种解决此问题的直接方法就是停止所有的工作,既 STW(stop the world),但是这会造成性能问题,还有一种就是引入屏障技术。

Dijkstra插入写屏障:防止黑色对象指向白色对象(把所有黑色对象指向的灰色对象和白色对象都变为黑色),不适用于栈空间(栈容量小,要求速度高),可以实现GC和用户程序并行,但是仍存在两个缺点:过于保守,可能会导致某些垃圾对象不被回收;对栈上对象来说,迪杰斯特拉插入写屏障要么在用户程序执行内存写操作时为栈上对象插入写屏障,要么在一轮三色标记完成后使用STW重新对栈上对象进行三色标记,前者会降低栈空间响应速度,后者会暂停应用程序。

传统的算法都有 STW 问题,三色标记和写屏障则避免了这个问题,在后面推出的增量式 GC 和并发式 GC 也是基于这个思路。

Go 有 GC,但是 GC 也不是万能的,在平时的编码还要注意 GC 调优,同时在合适的时候也可以调用 runtime.GC  来触发调优。

引用:

https://go.dev/blog/ismmkeynote

https://blog.csdn.net/qq_39382769/article/details/122207333

你可能感兴趣的:(浅谈Golang垃圾回收)