G1垃圾回收

G1(Garbage-First) 收集器是为服务器准备的垃圾回收器,是为很多核大内存服务器准备的垃圾回收器。它在达到高吞吐量时满足了GC停顿时间可预测这样的一个目标。在Oracle JDK 7 update4 及以后的版本被支持。G1收集器是为如下程序设计:

  • 垃圾收集于应用线程之间是并发的
  • 紧凑的自由空间且没有较长的GC停顿时间
  • 需要更可以预测的GC停顿时间
  • 不想牺牲很多吞吐性能
  • 不需要更大的Java堆

G1计划被当作CMS的长期的替代品。相比CMS,很多不同把G1变成一个更好的解决方案。

  • G1是一个压缩收集器,G1垃圾回收器依赖于区域(regions),避免使用空闲列表进行分配(avoid the use of fine-grained free lists for allocation)。这大大简化了垃圾回收器的各个部分,并消除了潜在的碎片问题
  • 对比CMS,G1垃圾收集器提供更加可预测的GC停顿时间,并允许用户指定暂停的目标(and allows users to specify desired pause targets)

G1垃圾回收器概述

旧的老年代收集器(serial,parallel,CMS)把堆分成了三个部分,年轻代,年老代,永久代,并且永久代是固定的内存大小

HeapStructure.png

所有内存对象都最终属于这三部分之一

G1垃圾收集器使用了不同的步骤

Slide9.png

堆被分成了相等大小的内存区域,每一个都是连续虚拟内存。某些区域被集合被分配成于旧回收器同样的角色(eden,surivivor,old)但他们的大小并没有固定。这在内存使用方面提供了更大的灵活性。

当执行垃圾回收的时候,G1操作在某方面和CMS类似。G1执行并发的全局标记,以决定堆内存里对象的存活性。在标记阶段完成之后,G1知道了那一个内存区域(regions)几乎为空,它会首先收集这些区域,这样通常回收出较大空白内存。这也是为什么这种垃圾回收器被叫做Garbage-First。顾名思义,G1优先在充满垃圾对象内存区域执行收集和压缩行动。G1使用暂停预测模型来满足用户自定义的暂停时间目标,并且根据制定暂停时间目标选择收集的收集的区域。

由G1标识哪些区域充满垃圾对象,G1从一个或多个区域区域拷贝对象到一个堆内存区域,在这个过程中压缩并清理内存。这个过程是在多个处理器上执行的,用以减少暂停时间并增加吞吐量。因此每个垃圾收集,G1都在用户定义的暂停时间内连续的工作以减少内存碎片。这个超出了前面两种方法的能力。CMS垃圾回收器不会压缩ParallelOld垃圾回收器仅执行整个堆压缩,这导致相当长的停顿时间

值得注意的是,G1不是一个实时的回收器。它有可能满足设定的目标时间,但不是绝对的。G1根据先前收集的数据,估算用户指定的目标时间可以收集多少区域。收集器有一个合理准确的垃圾收集模型,并使用此模型来确定要收集那些区域,同时stop-the-world暂停时间在用户指定的时间范围内。

注意:G1由并发(concurrent)(与应用程序一起运行,例如优化、标记、清理)和并行(parallel)(多线程)阶段。完整的垃圾回收仍然是单线程的,如果想调整jvm参数,您的应用程序应该避免fullGC。

G1足迹

如果你从ParallelOldGC或者CMS改成G1,你可以观察到G1 jvm进程占用资源更大。这在很大程度上与“记账”数据结构有关,例如Remembered Sets 和 Collection Sets。

Remembered Sets

Remembered Sets 或 Rset:将对象引用跟踪到给定区域中。堆中每个区域(region)有一个Rset。Rset可以并行和独立的收集一个区域的垃圾对象,Rset的对jvm占用内存大小不超过5%。

Collection Sets

Collection Sets或 CSets:在一个GC过程中CSets是一种将被回收的区域集。在GC过程中,所有在CSet中活着的对象将要被移动、复制。区域集合可以是Eden、survivor或者是年老代。CSet堆jvm内存占用大小不超过1%

G1的推荐用例

G1的首要重点是为运行需要大堆且GC延迟有限的应用程序的用户提供解决方案。 这意味着堆大小约为6GB或更大,并且稳定且可预测的暂停时间低于0.5秒。

如果当前具有CMS或ParallelOldGC垃圾收集器的应用程序具有以下一个或多个特征,则将其切换到G1很有用。

  • 完整的GC持续时间太长或太频繁。
  • 对象分配率或提升率差异很大。
  • 不必要的长时间垃圾收集或压缩暂停(长于0.5到1秒)

注意:如果您使用的是CMS或ParallelOldGC,并且您的应用程序未经历长时间的垃圾收集暂停,则可以继续使用当前的收集器。 使用最新的JDK不需要更改为G1收集器。