golang垃圾回收MySQL_Go语言——垃圾回收GC

Go语言——垃圾回收GC

参考:

引用计数:对每个对象维护一个引用计数,当引用该对象的对象被销毁时,引用计数减1,当引用计数器为0是回收该对象。

优点:对象可以很快的被回收,不会出现内存耗尽或达到某个阀值时才回收。

缺点:不能很好的处理循环引用,而且实时维护引用计数,有也一定的代价。

代表语言:Python、PHP、Swift

标记-清除:从根变量开始遍历所有引用的对象,引用的对象标记为"被引用",没有被标记的进行回收。

优点:解决了引用计数的缺点。

缺点:需要STW,即要暂时停掉程序运行。

代表语言:Golang(其采用三色标记法)

分代收集:按照对象生命周期长短划分不同的代空间,生命周期长的放入老年代,而短的放入新生代,不同代有不能的回收算法和回收频率。

优点:回收性能好

缺点:算法复杂

代表语言: JAVA

标记

在之前的Go语言——内存管理一文中,分析过span是内存管理的最小单位,所以猜测gc的粒度也是span。

type mspan struct {

// allocBits and gcmarkBits hold pointers to a span's mark and

// allocation bits. The pointers are 8 byte aligned.

// There are three arenas where this data is held.

// free: Dirty arenas that are no longer accessed

// and can be reused.

// next: Holds information to be used in the next GC cycle.

// current: Information being used during this GC cycle.

// previous: Information being used during the last GC cycle.

// A new GC cycle starts with the call to finishsweep_m.

// finishsweep_m moves the previous arena to the free arena,

// the current arena to the previous arena, and

// the next arena to the current arena.

// The next arena is populated as the spans request

// memory to hold gcmarkBits for the next GC cycle as well

// as allocBits for newly allocated spans.

//

// The pointer arithmetic is done "by hand" instead of using

// arrays to avoid bounds checks along critical performance

// paths.

// The sweep will free the old allocBits and set allocBits to the

// gcmarkBits. The gcmarkBits are replaced with a fresh zeroed

// out memory.

allocBits *gcBits

gcmarkBits *gcBits

}

021abe4ac70f7c478e1e3e955f6221b4.png

bitmap

如图所示,通过gcmarkBits位图标记span的块是否被引用。对应内存分配中的bitmap区。

三色标记

灰色:对象已被标记,但这个对象包含的子对象未标记

黑色:对象已被标记,且这个对象包含的子对象也已标记,gcmarkBits对应的位为1(该对象不会在本次GC中被清理)

白色:对象未被标记,gcmarkBits对应的位为0(该对象将会在本次GC中被清理)

例如,当前内存中有A~F一共6个对象,根对象a,b本身为栈上分配的局部变量,根对象a、b分别引用了对象A、B, 而B对象又引用了对象D,则GC开始前各对象的状态如下图所示:

初始状态下所有对象都是白色的。

接着开始扫描根对象a、b; 由于根对象引用了对象A、B,那么A、B变为灰色对象,接下来就开始分析灰色对象,分析A时,A没有引用其他对象很快就转入黑色,B引用了D,则B转入黑色的同时还需要将D转为灰色,进行接下来的分析。

灰色对象只有D,由于D没有引用其他对象,所以D转入黑色。标记过程结束

最终,黑色的对象会被保留下来,白色对象会被回收掉。

84dc648f8e07e7cee628a67e70f4c973.png

4

STW

stop the world是gc的最大性能问题,对于gc而言,需要停止所有的内存变化,即停止所有的goroutine,等待gc结束之后才恢复。

触发

阈值:默认内存扩大一倍,启动gc

定期:默认2min触发一次gc,src/runtime/proc.go:forcegcperiod

手动:runtime.gc()

go

Sweep Termination: 对未清扫的span进行清扫, 只有上一轮的GC的清扫工作完成才可以开始新一轮的GC

Mark: 扫描所有根对象, 和根对象可以到达的所有对象, 标记它们不被回收

Mark Termination: 完成标记工作, 重新扫描部分根对象(要求STW)

Sweep: 按标记结果清扫span

目前整个GC流程会进行两次STW(Stop The World), 第一次是Mark阶段的开始, 第二次是Mark Termination阶段.

第一次STW会准备根对象的扫描, 启动写屏障(Write Barrier)和辅助GC(mutator assist).

第二次STW会重新扫描部分根对象, 禁用写屏障(Write Barrier)和辅助GC(mutator assist).

需要注意的是, 不是所有根对象的扫描都需要STW, 例如扫描栈上的对象只需要停止拥有该栈的G.

从go 1.9开始, 写屏障的实现使用了Hybrid Write Barrier, 大幅减少了第二次STW的时间.

写屏障

因为go支持并行GC, GC的扫描和go代码可以同时运行, 这样带来的问题是GC扫描的过程中go代码有可能改变了对象的依赖树。

例如开始扫描时发现根对象A和B, B拥有C的指针。

GC先扫描A,A放入黑色

B把C的指针交给A

GC再扫描B,B放入黑色

C在白色,会回收;但是A其实引用了C。

为了避免这个问题, go在GC的标记阶段会启用写屏障(Write Barrier).

启用了写屏障(Write Barrier)后,

GC先扫描A,A放入黑色

B把C的指针交给A

由于A在黑色,所以C放入灰色

C没有子对象,放入黑色

扫描B,B没有子对象,放入黑色

即使A可能会在稍后丢掉C, 那么C就在下一轮回收。

开启写屏障之后,当指针发生改变, GC会认为在这一轮的扫描中这个指针是存活的, 所以放入灰色。

root

首先标记root跟对象,根对象的子对象也是存活的。

根对象包括:全局变量,各个G stack上的变量等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值