垃圾回收的几种机制
引用计数
过程:
1)当对象被创建时,初始化计数器为1
2)当更新指针时,新指向的对象引用计数加一,旧对象引用计数减一,如果为0则回收掉,放入空闲队列,并且递归的去 更新
优点:
1)实时性高,当对象引用计数为0时,立即回收
2)垃圾回收过程中应用无需挂起
缺点:
1)指针的更新需要手动的去操作,当前对象的引用计数为0时,需要递归更新对象的引用计数
2)无法解决循环引用问题(自循环引用、相互循环引用、多循环引用)
解决循环引用的方法:
1)避免产生循环引用
2)在合适的时机,手动显示的断开
3)弱引用 weak_ptr
标记清除
从root开始一层层扫描,root指当前所有goroutine的栈和全局数据区的变量(主要是这两个)
扫描过程中把所有可达的object标记出来,未被标记的object就需要清除,清除后的object会被放回到mcache中以备后续分配使用
整个过程需要stop the worl,挂起所有用户goroutine
三色标记
对于黑色的object,如果标记期间发生了写操作,写屏障会在真正赋值前将新对象标记为灰色
标记过程中,新分配的object,会先被标记成黑色再返回
对上的object被赋值给一个栈上的指针,写屏障是检测不到的,所以标记的最后阶段,还会回头重新扫描一下所有的栈空间,确保没有遗漏
混合写屏障
不需要最后回头重新扫描所有goroutine的栈空间了,是的整个GC过程中的stw可以忽略不计了
但也带来一些代价,如果C未被赋值给L,却被置灰,只能等下一次GC的时候再进行回收
还有一种情况,当GC过程中新创建的对象(被置黑),也会被下次回收
节点复制
分代收集(新生代和老年代)
将堆划分为两个多个称为代的空间,新创建的对象放在新生代中,随着垃圾回收的重复执行,生命周期长的对象会被提升到老年代中
golang的GC
1)1.3版本以前,go runtime在一定条件下(内存超过阈值或定期2min)暂停所有任务的执行,进行mark&sweep操作,操作完成后启动所有任务的执行,整个过程需要stw,挂起所有goroutine
2)1.3版本开始,go runtime分离了mark和sweep操作,也是先暂停所有任务执行并启动mark,mark完之后就重新启动被暂停的任务,让sweep任务和普通协程任务一样并行的一起执行。如果运行在多核处理器上,go会试图让gc任务放到单独的核心上运行,尽量不影响业务代码的执行,标记过程stw,清除过程并行
3)1.4版本,runtime很多代码取代了原生的c语言,采用go语言实现
4)1.5版本,三色标记清除垃圾收集器(非分代的、非移动的、并发的),三色标记法的mark操作是可以渐进执行的,而不需要每次都扫描整个内存空间,可以减少stop the world的时间,三色标记+写屏障,
5)1.8版本,Hibrid write Barrier(混合写屏障),不需要最后回头重新扫描所有goroutine的栈空间
GC何时被触发
1)堆上内存达到一定数值
2)每隔2min,强制触发
3)用户手动触发,使用runtime.GC()强制触发