JavaOne: Garbage First

  • Charles Humble
  • 张龙

2008 年 5 月 19 日

话题:JavaJVMDevOps语言 & 开发架构

Sun 的 HotSpot 垃圾收集器可分为两类:新生区(young generation)与老年区(tenured generation)。大部分的内存分配在新生区中进行,相对于垃圾收集的间隔时间来说,它经过了优化并且生命周期很短。经过几次垃圾收集后仍然存活于 新生区中的对象将被迁移到老年区中,这部分区域通常更大并且垃圾收集不那么频繁。新生区收集器分为连续式(Serial)、 同新式(ParNew)及并行扫描式(Parallel Scavenge)三种。所有这三种都是拷贝收集器。连续式使用了一个单独的 GC 线程,而同新式与并行扫描式都使用了多线程。老年区收集器都使用了标记扫 描压缩(mark-sweep-compact)算法。同样老年区收集器也分为三种:Serial Old(另一个单独的 GC 线程)、Parallel Old(使用多个 GC 线程)及 CMS(一个多并发低暂停的收集器)。Garbage First 的目标在于替换掉 CMS 并且采取了某些不同的方式——跨越了新生区和老年区的边界。    

在今年 JavaOne 的一个展示中,Tony Printezis 对 Garbage First 进行了详尽的介绍,在 JavaOne 大会的网站上有一个随后的采访。Printezis 概述了 Garbage First (G1)的工作方式:

“堆被切分成固定大小的区域,同时两个区域之间的分隔基本上是合理的。因此我们可以认为一些区域是新的,另一些是老的。在 G1 中所有的空间回收都是通过拷 贝完成的。G1 选择一组区域,从那些区域中摘出存活的对象,然后将其拷贝到另一组区域中。这就是 G1 中空间回收的方式,而不是 CMS 中所采取的那种方式 (拷贝与适当的重分配的组合方式)。” 

Printezis 继续阐述了新的收集器的三个主要目标:

“首要目标是随始终一致的低停顿率。本质上,由于 G1 在处理同时做压缩,它将对象从堆的一个地方拷贝到另一个地方。这样,由于压缩的原因,它不会遇到 CMS 可能会遇到的碎片问题。总会有连续空闲的空间供分配,这就使得 G1 拥有始终一致的停顿率。

第二个目标是尽量避免完全的 GC。在 G1 对全局进行标记并决定堆上对象的活跃度后,它立刻就知道堆上的哪些区域几乎是空闲的。它将首先处理那些区域,腾出 大量空间。通过这种方式,垃圾收集器将获得更多空间并减少完全 GC 的可能性。这也是为什么该垃圾收集器叫做 Garbage-First 的原因。

最后一个目标是良好的吞吐量。对于我们很多客户来说,吞吐量意味着一切。我们期望 G1 拥有良好的吞吐量以满足我们客户的需求。”

Sun 研究小组发表了一篇论文(pdf 格式)更加详尽地论述了 Garbage-First 并深入分析了如何实现这些目标,尤其是实时目标。大多数实时收集器工作在单个对象层次上,而 Garbage First 则在区域层次上进行收集。如果任何区域不再包含存活的对象时,它就会被立刻回收。用户可以为停顿率指定一个目标,G1 会基于之前的收集对此时可 回收的区域数量作出估计。该收集器对区域回收的代价有一个合理且精确的模型,所以“该收集器可以在给定的停顿时间内(高概率)选择一组可被回收的区域。” 换句话说,Garbage-First 并不是一个纯粹的实时收集器——它以高概率但不绝对地满足软实时目标。作为交换,Garbage-First 应该具 备更高的吞吐量以作为软实时的补偿,但是其仍会适度遵循实时的限制。这对于经常产生大量存活堆数据和线程级别数据的大规模服务器端应用来说是非常棒的。 Garbage-First 还提供了一些出色的控制,使得用户可以在垃圾收集的执行周期中指定一小部分时间——例如,在下一个 120 秒中最多花 20 秒的时 间在垃圾收集上。

Garbage First 将会包含在 Java SE 7 中并且过几周就会被提交。它也将以升级包的方式加入到 Java 6 中。

查看英文原文:JavaOne: Garbage First

JavaJVMDevOps语言 & 开发架构