使用G1垃圾收集器
自Oracle JDK 7 update 4之后,JVM中引入了一种新的垃圾收集器--G1垃圾收集器(Garbage-First Collector)。之前的一篇文章,有介绍一些基本的垃圾收集器,比如比较常用的CMS垃圾收集器,但即便是CMS垃圾收集器,其也有一些不足,比如,不能很好地处理内存碎片的问题,随着应用的运行,也会导致内存使用率不高,因此通常在使用CMS垃圾收集器时,会设置相对较大的堆大小。G1垃圾收集器则旨在能够比CMS垃圾收集器更合理地管理内存,或者说更智能地管理内存。本文将介绍有关G1垃圾收集器的基本原理,以便开发人员能考虑在生产中使用之。
G1垃圾收集器介绍
Garbage-First(G1)收集器是一种服务器(server)类型的垃圾收集器,针对具有大内存的多处理器机器。 大多数情况下,它能满足垃圾收集(GC)暂停时间目标,同时实现高吞吐量,这在之前的垃圾收集器中是无法满足的,从Oracle JDK 7 Update 4之后已完全支持G1垃圾回收器。G1垃圾收集器主要为有以下需求而设计:
G1垃圾收集器被计划作为CMS垃圾收集器的长期替代品。相较于CMS,G1的某些特性使其成为更好的解决方案,其一是G1属于压缩垃圾收集器,其没有使用CMS中的空闲内存链表来为对象分配内存,而是使用了区(Region)。这大大简化了垃圾收集器的工作,并且大部分消除了潜在的碎片问题。此外,G1提供了比CMS更可预测的垃圾收集暂停,并允许用户指定所需的暂停目标。
G1运行概览
对于一些旧的垃圾收集器(如串行垃圾收集器(Serial),并垃圾收集器(Parallel),CMS垃圾收集器),整个内存堆被分为了三个区域:Eden区,Survivor区及Old区,如图所示:

然而,G1采用了不同的堆分布,整个堆会被划分为一些列大小相等且内存连续的区(Region),某些区仍会被标记为旧垃圾收集器中的分代(年轻代,老年代及永久代),但这些代并没有固定的大小,因此,在内存使用上,具有更大的灵活性,如图所示:

当执行垃圾回收时,G1会以类似于CMS收集器的方式操作。G1并行执行全局标记阶段,以确定整个堆中存活的对象。 在标记阶段完成后,G1则知道哪些区域大多是空的,它首先在这些区域收集,这通常会产生大量的空闲内存,这就是为什么这种垃圾收集方法称为Garbage-First。 顾名思义,G1将其收集和压缩的动作集中在可能充满可回收对象(即垃圾)的区域。G1会使用暂停时间预测模型,来达到用户定义的暂停时间目标,并基于指定的暂停时间目标选择要收集的区域的数量。
由G1识别为可回收区域中的垃圾对象,会以evacuation(暂且叫淘汰)的方式被回收的,即将存活对象从堆的一个或多个区域复制到堆上的单个区域,并且在此过程中压缩和释放这些内存区域,该过程在多处理器上是并行执行的,以减少暂停时间,并增加吞吐量。因此,对于每次垃圾收集,G1将在用户定义的暂停时间内持续运行,以减少内存碎片,这具备了之前的垃圾收集器同时满足这两点的能力(如CMS不执行压缩操作,ParallelOld回收只执行整堆压缩,这会导致相当大的暂停时间)。
需要注意的是,G1并不是实时垃圾收集器,它只会尽最大可能但不是绝对确定性满足用户设置的暂停时间目标。通过来自先前垃圾收集的数据,G1会估计在用户指定的目标时间内可以收集多少区域。 因此,G1收集器对于收集区域,具有相当准确的的收集成本模型,并且会使用该模型来确定在停留时间目标内应该收集哪个区域和多少区域。
G1既有并发阶段(与应用程序线程一起运行,例如优化,标记,清除),也有并行阶段(多线程,例如stop-the-world)。FullGC仍然是单线程的,但如果调整得当,你的应用程序应该避免FullGC。
建议使用G1的场景
G1为运行在大堆上但又要求较低的GC延迟的应用提供了解决方案,这意味着堆大小约为6GB或更大,并且稳定和可预测的暂停时间低于0.5秒。如今使用CMS或ParallelOldGC垃圾收集器的应用如果具有一个或多个以下特性,可以考虑切换到G1: