无停顿的GC算法<翻译>

无停顿的GC算法

​ ·································译者:黄俊

摘要

现在对于响应时间敏感的应用受限于垃圾回收堆的大小。堆内存不断增加,GC暂停时间开始不断增加,导致了较高的响应时间。因此,一个可持续改进的,可伸缩的并发垃圾回收算法值得花时间去讨论它。

硅谷公司已经构建了一个可定制化的系统(CPU,芯片,主板,操作系统),使其能够在上运行特定的垃圾回收器的虚拟机。定制化的CPU包含读屏障【附录有我的通俗解释】指令。由于读屏障指令的存在我们可以构建一个高并发的,并行的,压缩GC算法(不需要stop world阶段)【附录有解释并行和并发】。这个无停顿的算法被设计来保证在GC的每个阶段,都让mutator 拥有着高吞吐量且不会中断mutator。

需要保证收集垃圾的速度大于分配内存的速度的基本要求,无停顿垃圾回收期从来不急于完成任何一个GC阶段。任何GC阶段都不会让mutator参与太多的事情。一些无停顿的算法拥有“自我修复”(注:就是引用的修正,一会儿你看到下面就知道了,别管这是啥- -)的能力,这将会降低mutator的开销,并且让mutator对GC状态不敏感。

我们介绍了无停顿GC算法,以及它所支持的硬件特性,并且给大家展示出运行持续工作负载时的开销、效率和暂停时间的数据。

【注意】:mutator 中文翻译叫突变体,但是这里指Mutator,至于为啥老外这么叫?因为这东西修改了堆内存和对象的引用,所以这么叫咯,习惯就好。

1、介绍

今天,许多企业的应用都运行在拥有垃圾回收的虚拟机上,如JVM,.NET。大部分应用都对于响应时间很敏感,例如,人们等待web页面的响应,或者信用卡的交易。不适时的GC暂停将会导致不能够接受的响应时间。对于这些应用程序,收集器以偶尔较差的响应时间(注:例如fullGC)为代价来换取平均较高的吞吐量这是不能接受的。

这些企业应用需要一个停顿时间短的垃圾回收器(让人们不能察觉的停顿时间:10-100ms)并且能够工作在非常大的java程序中(堆大小:100MB到100gb)且能够支撑很大的并发负载(100+个并发工作线程)。这样的收集器需要在长时间内执行一致且可预测的任务,而不是简单地在短时间内完成工作负载。

一些现代的垃圾回收器依赖于写屏障施加于Mutator对堆数据的写操作(注:想想对象的赋值,为啥需要写屏障?因为线程把对象引用修改了,我得告诉GC回收线程我改了这个引用吧,不然GC无感知的话,给你把对象干掉了,那就玩完了),通过写屏障来跟踪不同堆区域的对象引用。这可以高效的支持分代或基于区域的GC(G1,CMS等),并广泛用于许多垃圾收集语言,包括大多数生产环境的Java实现。另一方面,尽管已经有了大量的学术研究,但读屏障很少用于生产系统,因为它们通常给mutator带来很大的开销。

硅谷公司已经构建了一个可定制化的系统(CPU,芯片,主板,操作系统),使其能够在上面运行特定的垃圾回收器的虚拟机。定制化的CPU包含读屏障指令。由于读屏障指令的存在我们可以构建一个高并发的,并行的,压缩GC算法(不需要stop world阶段)。无停顿算法简单,高效(低mutator开销),并且无需要STW阶段。

2、相关工作

垃圾收集的概念已经存在很长时间了。我们不试图总结所有相关的内容GC工作,相反,我们建议读者参考几个GC调查报告和一些有亮点的文章。

GC暂停及其对Mutator不可预测的影响是并发收集器早期工作背后的驱动力。人们希望制作出专用的GC硬件,并且希望很快变得可行和普及。这项早期的工作需要很多广泛的、细粒度的同步操作,因此只有在专用硬件上才可行。GC硬件直到今天还在继续被提出。

使用通用的页面保护硬件来支持GC的想法的GC算法也有一段时间了。APPEL和OSSIA保护可能包含具有非转发指针的对象的页面(最初是所有页面)。访问一个受保护的页面将会导致OS陷入(陷入内核,上下文切换)GC通过转发该页上的所有指针来处理,并且之后清理页保护。Appel在内核模式下进行转发,Ossia使用第二个不受保护的虚拟地址来映射物理内存,以供GC使用。然而,我们的无停顿收集器只保护包含被移动对象的页面,而不是保护包含指向被移动对象的指针的页面。这是一个小得多的页面集,可以对页面进行增量保护。我们的读屏障允许我们拦截和纠正个别过时的引用,并避免阻塞Mutator来修正整个页面。我们也支持特殊的GC保护模式允许快速的、非内核模式的陷阱处理程序来处理受保护的页面。(注:专业词汇有点多,这里做一下解释,大家都知道OS为了保护内核和应用程序,分了两个态:内核态和用户态,而我们的JVM和GC都运行在用户态,而敏感操作都需要Trap到内核态执行,而陷入本身是非常耗时的,所以这里的设计为在用户态执行的trap称之为GC trap handler,也就是在用户态去执行GC的读屏障。Are U OK???)

增量收集器(通过引用计数)的概念也并不新鲜。增量收集器能够及时的进行垃圾回收以减少应用暂停时间,并且与应用程序一起工作。由于引用计数是昂贵的,而且实际上所有的屏障(引用计数通常涉及写屏障)都会变相的对Mutator产生一些成本,因此在降低屏障成本方面有着相当多的研究。还是那句话,在硬件中实现读屏障可以大大降低成本。在我们的例子中,成本大约是一个单周期ALU指令的成本。

增量式和低暂停时间收集器再次变得非常流行——部分原因是嵌入式设备的计算能力已经发展到可以在其上运行垃圾收集语言的程度。Metronome是一个现代的单处理器嵌入式系统的低暂停时间收集器的例子,并且Metronome报告的暂停时间确实比这里报告的小。但是,正如目前所描述的,Metronome是单线程的,大型的业务类应用程序有足够的mutator来淹没任何单线程收集器(这里指的是Mutator分配内存的速度大于单线程垃圾回收器回收的速度,那么想想会发生什么呢?)。无停顿回收器的回收线程是完全并行的,可以在任何时候添加GC工作线程。Metronome需要一个册子来预测未来运行应用程序的GC需求(注:也就是记账,预估Mutator需要多大的空间),在具有固定应用程序集的嵌入式系统中,很容易提供此册子(工程师运行有限的应用程序集并测量GC消耗)。当然,服务器通常没有固定的应用程序集,GC需求非常不可预测。在Metronome中Mutator的使用率约为50%。相比之下,我们的Mutator利用率接近98%,我们使用额外的CPU来完成收集工作。作为交换总要做点取舍,Metronome提供硬实时保证,而我们只提供软实时保证。(注:因为上面有册子由程序员来预估,人工基本不会出现差错,而机器就不一样了,再好的算法也不可能做到硬实时)

我们的读屏障用于baker风格的Relocate,在允许Mutator使用它之前加载被修正的值。我们把收集垃圾的工作集中在已知的大部分已经死亡的地区,类似于G1回收器。我们的Mark阶段使用增量更新样式,而不是Snapshot-At-The-Beginning (SATB)样式(注:G1用的SATB,会造成浮动垃圾)。SATB第一次做一个读操作的时候(通常是一系列依赖性的测试)需要一个适当昂贵的写屏障。无停顿收集器则不需要写屏障。

并发的GC回收器在大多数现代生产环境JVM中都是可用的。BEA的JRockit ,SUN的HotSpot和IBM的产品JVM都有并发收集器,我们使用每个供应商提供的最新可用的Java 1.4版本进行了测试。然而,在所有情况下,这些收集器都不是默认的。它们似乎不像并行收集器那样稳定,而且有时会在Mutator上增加大量开销。对于其中一些收集器情况更糟,事务吞吐量并不比默认收集器更好。

3、硬件支持

3.1 背景

Azul系统已经建立了一个自定义系统(CPU,芯片,板,和操作系统)专门运行特定的垃圾收集的虚拟机,比如基于SUN的HotSpot JVM。我们描述了实际的生产硬件,其中有实际成本的设计,开发和调试。因此,我们有强烈的欲望去设计简单而经济的硬件。最后,我们构建的定制的GC硬件非常小。

基本的CPU内核是一个64位的RISC(注:精简指令集),经过优化可以运行像Java这样的现代被管理的语言(内存管理等)。它是一个优秀的JIT对象,而不直接执行Java字节码。每个芯片都包含24个CPU,最多16个这样的芯片可以做到cache-coherent(缓存一致性)。在一个对称的内存空间中,最大的系统拥有384个CPU

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值