作者:镜萱

垃圾回收(Garbage Collection,简称GC)是编程语言中提供的自动的内存管理机制,自动释放不需要的对象,让出存储器资源,无需程序员手动执行。

Golang中的垃圾回收主要应用三色标记法,GC过程和其他用户goroutine可并发运行,但需要一定时间的STW(stop the world),STW的过程中,CPU不执行用户代码,全部用于垃圾回收,这个过程的影响很大,Golang进行了多次的迭代优化来解决这个问题。

  • 1.3以前的版本使用标记-清扫的方式,整个过程都需要STW。
  • 1.3版本分离了标记和清扫的操作,标记过程STW,清扫过程并发执行。
  • 1.5版本在标记过程中使用三色标记法。回收过程主要有四个阶段,其中,标记和清扫都并发执行的,但标记阶段的前后需要STW一定时间来做GC的准备工作和栈的re-scan。
  1. Sweep Termination: 收集根对象,清扫上一轮未清扫完的span,启用写屏障和辅助GC,辅助GC是将一定量的标记和清扫工作交给用户goroutine来执行,写屏障在后面会详细说明。
  2. Mark: 扫描所有根对象和根对象可以到达的对象,并标记它们
  3. Mark Termination: 完成标记工作,重新扫描部分根对象(要求STW),关闭写屏障和辅助GC
  4. Sweep: 按标记结果清扫对象
  • 1.8版本引入了混合写屏障机制(hybrid write barrier),避免了对栈re-scan的过程,极大的减少了STW的时间。

标记-清扫算法

标记-清扫算法是一种追踪式的垃圾回收算法,并不会在对象死亡后立即将其清除掉,而是在一定条件下触发,统一校验系统中的存活对象,进行回收工作。在Golang中,有以下几种情况可触发:

  • gcTriggerAlways: 强制触发GC
  • gcTriggerHeap: 当前分配的内存达到一定阈值时触发,这个阈值在每次GC过后都会根据堆内存的增长情况和CPU占用率来调整
  • gcTriggerTime: 当一定时间没有执行过GC就触发GC(2分钟)
  • gcTriggerCycle: runtime.GC()调用

标记-清扫分为两个部分,标记和清扫,标记过程会遍历所有对象,查找出死亡对象。我们可以使用指针的可达性可确认对象的存活,也就是说,如果存在一条从根出发的指针链最终可指向某个对象,就认为这个对象是存活的。这样,未能证明存活的对象就可以标记为死亡了。

根:是一个有限的指针集合,程序可不经过其他对象直接访问这些指针,堆中的对象被加载时,需要先加载根中的指针。在Go中,一般为goroutine自己的栈空间和全局栈空间。

标记结束后,再次进行遍历,清除掉确认死亡的对象。

三色标记法

将标记-清扫法使用三色抽象的方式来描述,可以方便我们理解回收器的正确性。

回收器通过将对象图划分为三种状态来指示其扫描过程。黑色对象为该对象及其后代已处理且该对象确认存活的,灰色对象为已经扫描到但未处理完成或者还需要再次处理的,白色对象为尚未扫描到或已经死亡的。

如图所示,回收器从根出发,扫描到可达对象后标记其为灰色,放入灰色队列,在扫描灰色对象引用的对象,将他们标记为灰色,自身标记为黑色。扫描全部结束后,剩余未被扫描到的对象留在白色队列中,表示已死亡。

标记过程需的要STW,因为对象引用关系如果在标记阶段做了修改,会影响标记结果的正确性。例如下图,灰色对象B中包含指向白色对象C的指针e,对象C尚未被扫描,此时,如有其他程序,将e指针从B对象中删除,并将指向对象C的新指针f插入到黑色对象A中,由于对象A早已完成扫描,对象C就会一直保持白色状态直到被回收。

可以看出,一个白色对象被黑色对象引用,是注定无法通过这个黑色对象来保证自身存活的,与此同时,如果所有能到达它的灰色对象与它之间的可达关系全部遭到破坏,那么这个白色对象必然会被视为垃圾清除掉。 故当上述两个条件同时满足时,就会出现对象丢失的问题。

如果这个白色对象下游还引用了其他对象,并且这条路径是指向下游对象的唯一路径,那么他们也是必死无疑的。

为了防止这种现象的发生,最简单的方式就是STW,直接禁止掉其他用户程序对对象引用关系的干扰,但是STW的过程有明显的资源浪费,对所有的用户程序都有很大影响,如何能在保证对象不丢失的情况下合理的尽可能的提高GC效率,减少STW时间呢?

在Golang中使用并发的垃圾回收,也就是多个赋值器与回收器并发执行,与此同时,应用屏障技术来保证回收器的正确性。其原理主要就是破坏上述两个条件之一。

屏障技术

当回收器满足下面两种情况之一时,即可保证不会出现对象丢失问题。

弱三色不变式:所有被黑色对象引用的白色对象都处于灰色保护状态(直接或间接从灰色对象可达)。 强三色不变式:不存在黑色对象到白色对象的指针。

强三色不变式很好理解,强制性的不允许黑色对象引用白色对象即可。而弱三色不变式中,黑色对象可以引用白色对象,但是这个白色对象仍然存在其他灰色对象对它的引用,或者可达它的链路上游存在灰色对象。

三色抽象除了可以用于描述对象的状态的,还可用来描述赋值器的状态,如果一个赋值器已经被回收器扫描完成,则认为它是黑色的赋值器,如果尚未扫描过或者还需要重新扫描,则认为它是灰色的赋值器。

在强三色不变式中,黑色赋值器只存在到黑色对象或灰色对象的指针,因为此时所有黑色对象到白色对象的引用都是被禁止的。 在弱三色不变式中,黑色赋值器允许存在到白色对象的指针,但这个白色对象是被保护的。

上述这些可以通过屏障技术来保证。

插入屏障

插入屏障拦截将白色指针插入黑色对象的操作,标记其对应对象为灰色状态,这样就不存在黑色对象引用白色对象的情况了,满足强三色不变式,如上图例中,在插入指针f时将C对象标记为灰色。Go1.5版本使用的Dijkstra写屏障就是这个原理,伪代码如下:

writePointer(slot, ptr):shade(ptr)*slot = ptr

在Golang中,对栈上指针的写入添加写屏障的成本很高,所以Go选择仅对堆上的指针插入增加写屏障,这样就会出现在扫描结束后,栈上仍存在引用白色对象的情况,这时的栈是灰色的,不满足三色不变式,所以需要对栈进行重新扫描使其变黑,完成剩余对象的标记,这个过程需要STW。这期间会将所有goroutine挂起,当有大量应用程序时,时间可能会达到10~100ms。

删除屏障

删除屏障也是拦截写操作的,但是是通过保护灰色对象到白色对象的路径不会断来实现的。如上图例中,在删除指针e时将对象C标记为灰色,这样C下游的所有白色对象,即使会被黑色对象引用,最终也还是会被扫描标记的,满足了弱三色不变式。这种方式的回收精度低,一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉。Yuasa屏障伪代码如下:

writePointer(slot, ptr):if (isGery(slot) || isWhite(slot))shade(*slot)*slot = ptr

在这种实现方式中,回收器悲观的认为所有被删除的对象都可能会被黑色对象引用。

混合写屏障

插入屏障和删除屏障各有优缺点,Dijkstra的插入写屏障在标记开始时无需STW,可直接开始,并发进行,但结束时需要STW来重新扫描栈,标记栈上引用的白色对象的存活;Yuasa的删除写屏障则需要在GC开始时STW扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象,但结束时无需STW。Go1.8版本引入的混合写屏障结合了Yuasa的删除写屏障和Dijkstra的插入写屏障,结合了两者的优点,伪代码如下:

writePointer(slot, ptr):shade(*slot)if current stack is grey:shade(ptr)*slot = ptr

这里使用了两个shade操作,shade(*slot)是删除写屏障的变形,例如,一个堆上的灰色对象B,引用白色对象C,在GC并发运行的过程中,如果栈已扫描置黑,而赋值器将指向C的唯一指针从B中删除,并让栈上其他对象引用它,这时,写屏障会在删除指向白色对象C的指针的时候就将C对象置灰,就可以保护下来了,且它下游的所有对象都处于被保护状态。 如果对象B在栈上,引用堆上的白色对象C,将其引用关系删除,且新增一个黑色对象到对象C的引用,那么就需要通过shade(ptr)来保护了,在指针插入黑色对象时会触发对对象C的置灰操作。如果栈已经被扫描过了,那么栈上引用的对象都是灰色或受灰色保护的白色对象了,所以就没有必要再进行这步操作。

Golang中的混合写屏障满足的是变形的弱三色不变式,同样允许黑色对象引用白色对象,白色对象处于灰色保护状态,但是只由堆上的灰色对象保护。由于结合了Yuasa的删除写屏障和Dijkstra的插入写屏障的优点,只需要在开始时并发扫描各个goroutine的栈,使其变黑并一直保持,这个过程不需要STW,而标记结束后,因为栈在扫描后始终是黑色的,也无需再进行re-scan操作了,减少了STW的时间。

golang 关闭gc 并手动gc_Golang垃圾回收 屏障技术相关推荐

  1. golang 关闭gc 并手动gc_Golang 防内存泄漏编码原则

    (给Go开发大全加星标) 来源:zmlgo https://www.cnblogs.com/share-ideas/p/11365511.html [导读]go 编程中,如果写法不当,可能引发内存泄漏 ...

  2. golang 关闭gc 并手动gc_Golang 大杀器之跟踪剖析 trace

    Go语言中文网,致力于每日分享编码.开源等知识,欢迎关注我,会有意想不到的收获! 在 Go 中有许许多多的分析工具,在之前我有写过一篇 Golang 大杀器之性能剖析 PProf 来介绍 PProf, ...

  3. GC.Collect如何影响垃圾回收

    根据垃圾回收的算法,对象在内存中是按代的方式存放的,通常情况下,当第0代沾满分配的空间的时候(比如是256k),GC就会启动去回收第0代对象,幸存的第0代对象会被放入第1代中去,第1代的对象要等到放满 ...

  4. c++对象回收问题_从垃圾回收解开Golang内存管理的面纱之三垃圾回收

    四.垃圾回收 终于说到垃圾回收了,我的初衷就是要搞明白垃圾回收的算法,谁知道衍生出来那么多东西,哈哈. 5.1 常见垃圾回收策略 所谓垃圾回收,即为释放我们不再使用的对象的内存,话不多说,我们一一分析 ...

  5. java_opts gc回收器_JVM之垃圾回收机制(GC)

    JVM之垃圾回收机制全解(GC)文章底部有思维导图,较为清晰,可参考 导读:垃圾回收是Java体系中最重要的组成部分之一,其提供了一套全自动的内存管理方案,要想掌握这套管理方案,就必须了解垃圾回收器的 ...

  6. system.gc会立即执行垃圾回收吗_JVM基础到实战03-垃圾回收概念

    1.为什么要进行垃圾回收? 不回收会造成内存泄漏 2.什么时候执行回收? 达到一定的比例,或者申请的内存超出了空闲内存,触发回收 3.如果是你,如何设计垃圾回收算法?就是回收哪些类型的对象 回收栈中没 ...

  7. GC算法-增量式垃圾回收

    概述 增量式垃圾回收也并不是一个新的回收算法, 而是结合之前算法的一种新的思路. 之前说的各种垃圾回收, 都需要暂停程序, 执行GC, 这就导致在GC执行期间, 程序得不到执行. 因此出现了增量式垃圾 ...

  8. GC算法-分代垃圾回收

    概述 分代垃圾回收并不是一个新的算法, 而是将之前的回收算法结合利用, 分场景使用. 简单来说, 分代垃圾回收的思路, 就是给每个对象都分配一个年龄, 年龄越大的, 活的越久, 被回收的概率就越小. ...

  9. java gc 对象可达性_JVM垃圾回收系列---GC Roots可达性分析

    JVM的垃圾回收对于Java开发人员来说是比较透明的,本文采用问答的形式进行展开,希望能够解释下垃圾回收的一些问题.那么首先第一个问题 问:什么样的对象会被回收. 答:已经死亡的对象,不可达的对象,肯 ...

最新文章

  1. Oracle 11g 新特性 -- Transparent Data Encryption (透明数据加密TDE) 增强 说明
  2. 「模型解读」GoogLeNet中的inception结构,你看懂了吗
  3. windows php swoole 安装
  4. dbnetlib sqlserver不存在或拒绝访问_部署IIS+PHP+SQL server环境
  5. python if not A 和 if A is None
  6. matlab进阶摸索篇——彩色图直方图均衡化
  7. 计算机广告制作专业范围,计算机广告制作专业
  8. 2022山东省安全员C证考试试题及答案
  9. 20余年互联网沉浮史:剩者为王
  10. Java LeetCode每日一题-从易到难带你领略算法的魅力(七):Z 字形变换
  11. SRT编码器之Rendezvous模式详解
  12. 国家开放大学《国际经济法》第二章 国际货物买卖法 边学边练
  13. Python爬虫获取豆瓣电影TOP250
  14. Container With Most Water-水桶装水问题
  15. css 字体图标更改颜色_在CSS中更改字体
  16. 联机棋类游戏《憋尿罐》实现源码
  17. 力天创见客流、热区系统WEB管理
  18. 软件行业职位英文缩写介
  19. Docker下载速度慢,怎么解决?
  20. mybatis XML 中大于等于小于等于的写法

热门文章

  1. java编译使用androidsdk,详解Android源码的编译
  2. vmware esxi主机经常出现的警示“vsphere distributed switch mtu支持状态, vlan中继状态”求大神回复。
  3. vSAN其实很简单-vSAN禁得起折腾的吗?
  4. Linux学习总结(64)——DBA常用的Linux命令汇总
  5. Java基础学习总结(131)——项目开发中真的有必要使用Lombok插件么?
  6. java实现团购功能_[Java教程]jquery组件团购倒计时功能
  7. Excel的html转换成unicode,EXCEL表格中把汉字转换成unicode编码
  8. Oracle12c 的安装教程图解(安装系统:windows 2008R2)
  9. 毫秒数据字符串转换为DateTime
  10. 新买win8更换win7的方法步骤