Go内存回收

GC内存回收


内容都是学习小徐的公众号以及B站记录的笔记,个人理解,仅供参考

GC回收的利弊

GC指让垃圾回收器以守护者协程的方法在后台运作,按照既定策略自动回收、释放内存。
优势:
最大的好处就是方便开发者,屏蔽了内存回收的细节
另外就是团队开发的时候,由于缺乏全局视野,可能某个临界资源谁都不敢释放,结果就是没有释放,GC拥有全局视野
劣势:
GC提高了下限,但降低了下限。因为开发者失去了控制权,相当于阉割了自由度;
GC需要额外的空间存储状态信息,并且虽然GC大部分时间都可以并发,但还是存在STW,所以GC需要额外成本;

常见GC方法

  1. 标记清扫法

相当于排除法,将存活对象标记,未标记的就是需要清扫的。 问题:清扫后存在内存碎片

  1. 标记压缩

在标记清扫的基础上多了一步压缩,解决了内部碎片的问题。问题:性能上时间复杂度高

  1. 半空间复制(java新生代内存的GC方法)

将空间分外两半,一半fromspace,一半tospace;每次GC时把存活对象迁移到tospace,然后置换fromspace 跟tospace; 问题:空间利用率只有50%,因为右50%一直处于备用状态;(java使用分代内存管理,新生代被认为是GC高频的,采用半空间复制,而老代抗过多轮GC的被认为是低频GC的,低频GC的对象采用标记清扫)

  1. 引用计数

有点类似引用指针或者linux下的node计数器,计数为0的时候说明不被用户关心了,GC时清除计数为0的;问题:可能存在循环引用的问题

三色标记清扫法

属于标记清扫法的一种实现。通过黑、灰、白三种颜色代表对象的状态。 黑色表示对象存活,并且它指向的对象也已经被标记;灰色表示对象存活,但它指向的对象还没有被标记; 白色表示对象还未遍历;

  1. 首先根对象(全局对象、栈上局部变量)标记为黑色,并且根对象的指向对象全部置灰
  2. 将灰色对象所指向的对象全部置灰,然后灰色对象变黑;
  3. 重复以上过程,标记结束后,依然为白色的就是要被清扫的;

Go 1.5之前通过STW暂停全局用户协程,专注完成GC协程工作后再恢复全局用户协程,简单,但是用户会感受到明显的性能割裂感;
image.png
Go1.5之后,Go使用并发三色标记,GC协程跟全局用户协程是并发进行的。但要做到并发,必须解决几个问题。

并发后三色标记法会出现的问题

  1. 漏标问题:(用户让灰色失去白色,黑色得到白色)漏标导致清扫掉了不应该被清扫的对象。GC协程开始GC,灰色B一开始指向白色C,此时用户携程让B删掉C的引用,让A建立对C的引用,然而还在GC的过程中,这就会导致被黑色A引用的白色C颜色不会变灰,那么C就会被清扫。

image.png
漏标问题通过混合写屏障机制解决。

  1. 多标问题:(黑色A已经标记灰色B,用户再黑色A失去灰色B)GC协程下,黑色A引用的对象B先被置灰,然后用户删除A对B的引用,而此时B已经变成了灰色,导致这一轮GCB不会被删除。 多标问题可以接受,因为它只是多抗了一轮GC。

另外goGC的问题:
标记清扫的碎片问题,golang在内存分配的时候使用等级制度,从而将碎片问题局限在内部碎片上。
为何不使用java的分代内存GC:go使用了内存逃逸分析,在编译过程中把生命周期长的放到堆区,短的放到栈区(不通过GC内存回收)

屏障机制解决漏标问题

漏标问题是由于GC协程跟用户协程并发,用户协程操作导致灰色B失去白色C,并且黑色A引用白色C
解决漏标问题的方法论是强弱三色不变式,强三色:禁止黑色引用白色。 弱三色:黑色可以引用白色,前提是灰色不失去白色的引用。

插入屏障(实现强三色不定式)
当黑色对象要引用一个白色对象时,把白色对象置为灰色再引用。

删除屏障(实现弱三色不定式)
当一个白色对象即将被上游删除引用前,会触发屏障将其置灰,之后再删除上游指向其的引用。

混合写屏障
由于栈上的操作频繁,而屏障机制相当于回调函数,所以栈使用屏障机制会造成性能下滑,因此通过混合写屏障机制。GC开始前短暂的STW,将栈上的对象全部置黑(可达对象置灰);GC期间栈上新建的对象默认为黑;堆上正常使用屏障机制。 因此混合写屏障机制大大降低了STW,只有GC开始前有一段STW

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值