下面首先我们会聊一下什么是GC (垃圾回收),GC的作用是什么,然后再结合图示用每个人都能听懂的大白话解释Go的GC原理。
现代高级编程语言管理内存的方式分为两种:自动和手动,像C、C++ 等编程语言使用手动管理内存的方式,工程师编写代码过程中需要主动申请或者释放内存;而 PHP、Java 和 Go 等语言使用自动的内存管理系统,有内存分配器和垃圾收集器来代为分配和回收内存,其中垃圾收集器就是我们常说的GC。
在应用程序中会使用到两种内存,分别为堆(Heap)和栈(Stack),GC负责回收堆内存,而不负责回收栈中的内存。那么这是为什么呢?
主要原因是栈是一块专用内存,专门为了函数执行而准备的,存储着函数中的局部变量以及调用栈。除此以外,栈中的数据都有一个特点——简单。比如局部变量不能被函数外访问,所以这块内存用完就可以直接释放。正是因为这个特点,栈中的数据可以通过简单的编译器指令自动清理,并不需要通过 GC 来回收。
主流的垃圾回收算法有两大类,分别是追踪式垃圾回收算法和引用计数法( Reference counting )。而Go语言现在用的三色标记法就属于追踪式垃圾回收算法的一种。
追踪式算法的核心思想是判断一个对象是否可达,一旦这个对象不可达就可以在垃圾回收的控制循环里被 GC 回收了。那么我们怎么判断一个对象是否可达呢?很简单,第一步找出所有的全局变量和当前函数栈里的变量,标记为可达。第二步,从已经标记的数据开始,进一步标记它们可访问的变量,以此类推。
Go的垃圾收集器从一开始到现在一直在演进,在v1.5版本开始三色标记法作为垃圾回收算法前使用Mark-And-Sweep(标记清除)算法。从v1.5版本Go实现了基于三色标记清除的并发垃圾收集器,大幅度降低垃圾收集的延迟从几百 ms 降低至 10ms 以下。在v1.8又使用混合写屏障将垃圾收集的时间缩短至 0.5ms 以内。
Mark-And-Sweep,这个算法就是严格按照追踪式算法的思路来实现的。这个垃圾回收算法的执行流程可以用下面这张图来表示。
垃圾回收--标记清除
此算法主要有两个步骤:
这个算法最大的问题是 GC 执行期间需要把整个程序完全暂停,不能异步地进行垃圾回收,对实时性要求高的系统来说,这种需要长时间挂起的标记清扫法是不可接受的。所以就需要一个算法来解决 GC 运行时程序长时间挂起的问题。
从v1.5版本Go实现了基于三色标记清除的并发垃圾收集器,注意三色标记这个算法不是Go的垃圾收集器独有的。这个算法背后的核心思想是由Edsger W. Dijkstra,Leslie Lamport,A.J.Martin,C.S.Scholten和E.F.M.Steffens提出的,算法首先于1978年发表在论文*On-the-fly Garbage Collection:An Exercise in Cooperation*[1]上面。三色标记清除算法背后的首要原则就是它把堆中的对象根据它们的颜色分到不同集合里面。
三色标记算法将程序中的对象分成白色、黑色和灰色三类:
文字解释起来不太好理解,我用下面几张图演示一下三色标记清除的整个过程:
第一步:在进入GC的三色标记阶段的一开始,所有对象都是白色的。
第二步, 遍历根节点集合里的所有根对象,把根对象引用的对象标记为灰色,从白色集合放入灰色集合。
第三步, 遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合 第四步:重复第三步, 直到灰色集合中无任何对象。 第五步:回收白色集合里的所有对象,本次垃圾回收结束。
这里所说的根节点集合里的根对象就是栈上的对象或者堆上的全局变量。
Go
在GC阶段执行三色标记前,还需要先做一个准备工作——打开写屏障(Write Barrier)。那么写屏障是什么呢?我们知道三色标记法是一种可以并发执行的算法。所以在GC运行过程中程序的函数栈内可能会有新分配的对象,那么这些对象该怎么通知到 GC,怎么给他们着色呢?如果还是按照之前新建的对象标记为白色就有可能出现下图中的问题:
在GC进行的过程中,应用程序新建了对象I
,此时如果已经标记成黑的对象F
引用了对象I
,那么在本次GC执行过程中因为黑色对象不会再次扫描,所以如果I
着色成白色的话,会被回收掉,这显然是不允许的。
这个时候就需要我们的写屏障出马了。写屏障主要做一件事情,修改原先的写逻辑,然后在对象新增的同时给它着色,并且着色为灰色。因此打开了写屏障可以保证了三色标记法在并发下安全正确地运行。那么有人就会问这些写屏障标记成灰色的对象什么时候回收呢?答案是后续的GC过程中回收,在新的GC过程中所有已存对象就又从白色开始逐步被标记啦。
想要在并发或者增量的标记算法中保证正确性,我们需要达成以下两种三色不变性(Tri-color invariant)中的任意一种:
垃圾收集中的屏障技术更像是一个钩子方法,它是在用户程序读取对象、创建新对象以及更新对象指针时执行的一段代码,根据操作类型的不同,我们可以将它们分成读屏障(Read barrier)和写屏障(Write barrier)两种,因为读屏障需要在读操作中加入代码片段,对用户程序的性能影响很大,所以编程语言往往都会采用写屏障保证三色不变性。
在Go
语言 v1.7 版本之前,使用的是Dijkstra
插入写屏障保证强三色不变性,但是运行时并没有在所有的垃圾收集根对象上开启插入写屏障。因为 Go 语言的应用程序可能包含成百上千的 goroutine,而垃圾收集的根对象一般包括全局变量和栈对象,如果运行时需要在几百个 goroutine 的栈上都开启写屏障,会带来巨大的额外开销,所以 Go 团队在实现上选择了在标记阶段完成时暂停程序、将所有栈对象标记为灰色并重新扫描,在活跃 goroutine 非常多的程序中,重新扫描的过程需要占用 10 ~ 100ms 的时间。
Go 语言在 v1.8 组合 Dijkstra 插入写屏障和 Yuasa 删除写屏障构成了如下所示的混合写屏障,该写屏障会将被覆盖的对象标记成灰色并在当前栈没有扫描时将新对象也标记成灰色:
writePointer(slot, ptr):
shade(*slot)
if current stack is grey:
shade(ptr)
*slot = ptr
为了移除栈的重扫描过程,除了引入混合写屏障之外,在垃圾收集的标记阶段,我们还需要将创建的所有新对象都标记成黑色,防止新分配的栈内存和堆内存中的对象被错误地回收,因为栈内存在标记阶段最终都会变为黑色,所以不再需要重新扫描栈空间。
Go的垃圾回收器在使用了三色标记清除算法和混合写屏障后大大减少了暂停程序(STW)的时间,主要是在开启写屏障前和移除写屏障前暂停应用程序。
Go的垃圾收集的整个过程可以分成标记准备、标记、标记终止和清除四个不同阶段,每个阶段完成的工作如下:
_GCmark
、开启写屏障、用户程序协助(Mutator Assiste)并将根对象入队;在标记开始的时候,收集器会默认抢占 25% 的 CPU 性能,剩下的75%会分配给程序执行。但是一旦收集器认为来不及进行标记任务了,就会改变这个 25% 的性能分配。这个时候收集器会抢占程序额外的 CPU,这部分被抢占 goroutine 有个名字叫 Mark Assist。而且因为抢占 CPU的目的主要是 GC 来不及标记新增的内存,那么抢占正在分配内存的 goroutine 效果会更加好,所以分配内存速度越快的 goroutine 就会被抢占越多的资源。
除此以外 GC 还有一个额外的优化,一旦某次 GC 中用到了 Mark Assist,下次 GC 就会提前开始,目的是尽量减少 Mark Assist 的使用,从而避免影响正常的程序执行。
_GCmarktermination
并关闭辅助标记的用户程序;_GCoff
开始清理阶段,初始化清理状态并关闭写屏障;清理这个过程是并发进行的。清扫的开销会增加到分配堆内存的过程中,所以这个时间也是无感知的,不会与垃圾回收的延迟相关联。
Go语言的垃圾收集的实现非常复杂,难懂的技术概念和原理也比较多,这篇文章意在用每个人都能看懂的白话文字结合图示把Go的垃圾回收原理解释清楚,让读者能对垃圾回收的大体流程有个概念。
下面用一句话总结概况Go的垃圾回收原理:
Go的GC最早期使用的回收算法是标记-清除算法,该算法需要在执行期间需要暂停应用程序(STW),无法满足并发程序的实时性。后面Go的GC转为使用三色标记清除算法,并通过混合写屏障技术保证了Go并发执行GC时内存中对象的三色一致性(这里的并发指的是GC和应用程序的goroutine能同时执行)。
一次完整的垃圾回收会分为四个阶段,分别是标记准备、标记、结束标记以及清理。在标记准备和标记结束阶段会需要 STW,标记阶段会减少程序的性能,而清理阶段是不会对程序有影响的。
垃圾收集器[2]
Go 垃圾回收——三色标记法[3]
[1]On-the-fly Garbage Collection:An Exercise in Cooperation: https://www.microsoft.com/en-us/research/publication/fly-garbage-collection-exercise-cooperation/
[2]垃圾收集器: https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-garbage-collector/
[3]Go 垃圾回收——三色标记法: https://zhuanlan.zhihu.com/p/105495961
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/IxbJx_m4zRxmc_vcuCoWEQ
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。