Go GC: Prioritizing low latency and simplicity

xiaohu50 · · 2580 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

https://blog.golang.org/go15gc

在开始之前,先来看一个关于Go的垃圾回收在知乎的讨论
http://www.zhihu.com/question/21615032

The Setup

Go 正在构建一个面向未来的现代垃圾收集器(garbage collector, GC):一个不仅能够适应当下软件开发规模,还能支持下个十年的软件和硬件能力的长久GC。目前的全停式(stop-the-world)GC已经阻碍了go等安全性语言更加广泛的运用,在将来,或许已经没有全停式GC的立足之地。
Go 1.5作为对未来的惊鸿一瞥,达成了我们一年前设置的10毫秒GC延迟的目标。我们在a talk at Gophercon展示了一些令人振奋的数据。其中的延迟改进尤其收到了广泛关注;Robin Verlangen发表的博客Billions of requests per day meet Go 1.5让我们看到了使用者对于新GC的反馈。同时我们也特别满足于Alan Shreve’s productoin server graphs中其对“天啊,85%的延迟减少!!!”的赞美。

如今16G的内存只需要100美元,CPU也拥有越来越多的核,越来越多的超线程。十年之后当下的硬件可能会变成古董般的陈设,用Go语言搭建的软件也需要横向扩展以适应更多的需求和挑战。在那样的硬件基础上将有提升生产力的能力,Go的垃圾回收设计之初就考虑到了第延迟和易操控的的店。Go 1.5 是向那个方向前进的第一步,这一步将会永远的影响Go和相关应用的发展。此篇博客将会系统的介绍我们在Go 1.5的垃圾回收器中所做的努力。

The Embellishment

为了构建一个面向未来的GC,我们将目光朝向了一个几十年前出现的算法。Go的新GC是一个并发(concurrent),三色(tri-color),标记清除(mark-sweep)收集器,源自于Dijkstra in 1978的思路。由此思路引申的最大花费等级垃圾的收集器,我们相信这个算法能够适应现代硬件和低延迟软件系统的需求。

在三色收集器中,每个对象有都分属于三种颜色(白色,灰色和黑色)之一。我们将对中的对象看待成一张连通图。每次GC开始之初,所有的对象都是白色的。GC访问那些根节点对象,包括全局对象,栈中的对象,把他们标记为灰色。然后GC选择一个灰色对象,把它标记改成黑色,然后沿着指向其他对象的指针去访问其他对象。当其访问到一个白色对象,就将这个白色对象标记成灰色。过程重复直到再没有灰色对象。此时,剩下的白色对象就是不可达的,同时也是应该回收的。(关于三色标记清除的更多知识可以参阅维基百科)。

这一切都在并发的进行,我们可以把每个操作点看作一个突变点,在收集器运行的过程中改变访问指针。因此,突变点需要在操作过程中维持一个不变量——没有黑色对象指向白色对象,以免GC丧失对堆中已访问元素的追踪。维持这个不变量是由写屏障来实现的(本身是由突变点在修改指向指针的过程中所执行的一小段程序)。Go的写屏障将所有现在已达的所有白色节点置为灰色,保证GC最后能够访问到所有能访问的节点。

保持突变点不阻塞的状态下,判断遍历工作是否完成是一件复杂且精妙的工作。为了简化这个过程,Go 1.5采用尽可能多的并发,然后短暂地全停来检查所有潜在的灰色节点。找到平衡全停时间和GC工作量之间的甜点是一个可由Go 1.6复用的成果。

当然其中的困难在于实现的细节。比如何时GC开始遍历?如何确定开始遍历?GC该如何与Go调度器交互?我们如何去暂停一个运行了足够长时间突变进程去检查它的栈?我们怎样表示白灰黑三种状态以使我们高效的找到其中的灰色对象?如何找到遍历的根节点?如何找到对象引用指针?如何最小化内存碎片?如何优化缓存性能?堆该设置多大?凡此种种,有的在于分配,有的在于查找,有的在于调度,更多的在于性能。关于这些更加底层的话题的讨论已经超出了本文的范畴。

从顶层来看,一个解决性能问题的方案是增加GC的抓手,另一个是提升每个抓手的性能表现。前者编程者可以在他们的应用中调整抓手。缺点就是每过几年添加一两个新的抓手,然后每年你都陷入GC 抓手部署的重复劳动当中。Go并不准备那么做。相反,我们只提供一个GC抓手,称之为GOGC。这个值控制着你的堆栈的大小,数值相对于你目前可以触及的对象的大小。默认值100表示当前的堆空间比上次回收时的可以触及的对象大小要大上100%。200意味着当前堆空间比上次回收对象大小要大上200%(即3倍大小)。如果你想减少花费在GC上的时间,增加GOGC即可。如果你想用更多的GC时间来减少内存的时候,则降低GOGC。

更加重要的一点是,随着下代硬件的发展,内存很快就会翻倍,简单的增加GOGC的大小会减半GC的周期。另一方面,由于GOGC代表的是目前可达对象数目的相对值,可达对象数目变化并不需要你重新调整该参数。应用会自动调整规模。而且,没有持续增加对各种抓手的支持这样的重担,运行时团队可以把精力集中根据应用方反馈在提升运行时效的主要工作上。

The Punchline

Go 1.5的GC开辟了全停式GC不再是语言安全可靠的障碍。它向人们展示了随着硬件的发展强大,软件应用可以不费劲地迁移到新的硬件上,GC并不会成为其阻碍。未来十年以及更远的将来都将受益于此。更多关于Go 1.5的GC以及我们如何解决延时问题可以参阅GO GC: Latency Problem Solved presentation 或者 讲义


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:xiaohu50

查看原文:Go GC: Prioritizing low latency and simplicity

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2580 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传