[每周一更]-(第50期):Go的垃圾回收GC

参考文章:

什么是GC?

Go语言的垃圾回收机制Garbage Collection,简称 GC

在传统编程语言中我们需要关注对象的分配位置,要自己去选择对象分配在堆还是栈上,

但在 Go 这门有 GC 的语言中,集成了逃逸分析功能,帮助我们自动判断对象应该在堆上还是栈上,可以使用 go build -gcflags="-m" 来观察逃逸分析的结果

GC的原理是什么?

Go语言的垃圾回收器采用了并发三色标记清除算法(Concurrent Tri-Color Mark-And-Sweep),(Stop-the-World,简称STW,指的是GC事件发生过程中,会产生应用程序的停顿。)尽可能减少STW(stop the world)时间,

以降低吞吐为代价换取低延迟,实现了高效的垃圾回收。

标记清除算法的基本原理是,垃圾回收器将所有的存活对象标记为"活"的,未被标记的对象则被认为是垃圾。经典的标记清除算法通常分为两个阶段:

  • 标记阶段:垃圾回收器从根对象开始,遍历所有可达对象,并将它们标记为"活"的。
  • 清除阶段:垃圾回收器从堆的起始地址开始遍历,将未被标记的对象清除,回收内存。

Go语言的垃圾回收器采用了三色标记法(Tri-Color Marking),将堆上的内存对象分为三种颜色

  • 白色:未被标记为"活"的对象,是潜在的垃圾,后续可能会被GC回收。
  • 灰色:待扫描的对象,当扫描某个灰色对象时,GC会将其标记为黑色,然后将该对象指向的所有对象都标记为灰色,待后续标记。
  • 黑色:被标记为"活"的对象,在这轮GC中不会被回收。

垃圾回收器开始工作时不存在黑色对象,垃圾回收器会将根对象标记为灰色,并从根对象(通常是栈对象和全局对象)开始遍历。垃圾回收器会将灰色对象标记为黑色,

并将该对象指向的对象标记为灰色。垃圾回收器重复这个过程,直到所有可达对象都被标记为黑色。最后,垃圾回收器清除所有未被标记为黑色的对象,即清除所有白色对象。

GC如何优化?

优化GC的开销是提高系统性能和响应速度的重要手段。

缩短STW时间

Go GC触发时机大体分为三种:

  • 手动触发:调用runtime.GC()
  • 常规触发:Target heap memory = Live heap + (Live heap + GC roots) * GOGC / 100
  • sysmon后台周期性强制触发GC
减少堆内存的分配和释放

GC开销大的根源在于heap object多,Go的每轮GC都是FullGC,每轮都要将所有heap object标记(mark)一遍,

即便大多数heap object都是长期alive的,因此,一个直观的降低GC开销的方法就是减少heap object的数量,即减少alloc。

  • 把小对象聚合到一个结构体中,然后做一次分配即可
  • 内存空间重用,如:sync.Pool

常见的GC

  • 引用计数法
    根据对象自身的引用计数来回收,当引用计数归零时进行回收,但是计数频繁更新会带来更多开销,且无法解决循环引用的问题。
    • 优点:简单直接,回收速度快
    • 缺点:需要额外的空间存放计数,无法处理循环引用的情况;
  • 标记清除法
    标记出所有不需要回收的对象,在标记完成后统一回收掉所有未被标记的对象。
    • 优点:简单直接,速度快,适合可回收对象不多的场景
    • 缺点:会造成不连续的内存空间(内存碎片),导致有大的对象创建的时候,明明内存中总内存是够的,但是空间不是连续的造成对象无法分配;
  • 复制法
    复制法将内存分为大小相同的两块,每次使用其中的一块,当这一块的内存使用完后,将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉
    • 优点:解决了内存碎片的问题,每次清除针对的都是整块内存,但是因为移动对象需要耗费时间,效率低于标记清除法;
    • 缺点:有部分内存总是利用不到,资源浪费,移动存活对象比较耗时,并且如果存活对象较多的时候,需要担保机制确保复制区有足够的空间可完成复制;
  • 标记整理
    标记过程同标记清除法,结束后将存活对象压缩至一端,然后清除边界外的内容
    • 优点:解决了内存碎片的问题,也不像标记复制法那样需要担保机制,存活对象较多的场景也使适用;
    • 缺点:性能低,因为在移动对象的时候不仅需要移动对象还要维护对象的引用地址,可能需要对内存经过几次扫描才能完成;
  • 分代式
    将对象根据存活时间的长短进行分类,存活时间小于某个值的为年轻代,存活时间大于某个值的为老年代,永远不会参与回收的对象为永久代。
    并根据分代假设(如果一个对象存活时间不长则倾向于被回收,如果一个对象已经存活很长时间则倾向于存活更长时间)对对象进行回收。
相关推荐
意倾城10 分钟前
Spring Boot 配置文件敏感信息加密:Jasypt 实战
java·spring boot·后端
火皇40510 分钟前
Spring Boot 使用 OSHI 实现系统运行状态监控接口
java·spring boot·后端
rylshe131429 分钟前
在scala中sparkSQL连接mysql并添加新数据
开发语言·mysql·scala
小宋加油啊29 分钟前
Mac QT水平布局和垂直布局
开发语言·qt·macos
薯条不要番茄酱42 分钟前
【SpringBoot】从零开始全面解析Spring MVC (一)
java·spring boot·后端
MyhEhud1 小时前
kotlin @JvmStatic注解的作用和使用场景
开发语言·python·kotlin
想睡hhh1 小时前
c++进阶——哈希表的实现
开发语言·数据结构·c++·散列表·哈希
Clown951 小时前
Go语言爬虫系列教程(一) 爬虫基础入门
开发语言·爬虫·golang
Watermelo6171 小时前
前端如何应对精确数字运算?用BigNumber.js解决JavaScript原生Number类型在处理大数或高精度计算时的局限性
开发语言·前端·javascript·vue.js·前端框架·vue·es6
Aric_Jones3 小时前
lua入门语法,包含安装,注释,变量,循环等
java·开发语言·git·elasticsearch·junit·lua