Serial收集器
使用单线程工作的收集器,但它的“单线程”的意义并不仅仅是说明它只会使用一个处理器或者一条线程去完成垃圾收集工作、更重要的是强调它在垃圾收集时,必须暂停其它所有工作线程,直到收集结束。它是HotSpot虚拟机运行运行在客户端模式下的默认收集器,有着由于其它收集器的地方,那就是简单而高效(与其它单线程的收集器相比),对于内存资源受限的环境,它是所有收集器里额外内存消耗最小的,在单核处理器或处理器核心数较少的环境来说,Serial收集器由于没有线程开销,专心做垃圾收集自然可以获得最高的单线程收集效率。
ParNew收集器
ParNew收集器是Serial收集器的并发版本,除了支持多线程并行收集之外,其他与Serial收集器相比并没有太多创新之处。在单核心处理器上Serial收集器的效果更好。可以使用 -XX:ParallelGCThreads 参数设置垃圾收集的线程的数量。
Parallel Scavenge收集器
Parallel Scavenge收集器是一款新生代收集器,同样采用标志-复制算法实现收集器,也是能够并行收集的多线程收集器。Parallel Scavenge收集器的目标是达到一个可控吞吐量(Throughput)。所谓的吞吐量就是处理器用于运行用户代码时间与处理器总消耗时间的比值。-XX:MaxGCPauseMillis设置最大垃圾收集时间,-XX:GCTimeRatio设置吞吐量大小,-XX:UseAdaptiveSizePolicy设置是否自动调节停顿时间和吞吐量。
Serial Old收集器
Serial Old收集器是Serial的老年代版本,同样使用单线程收集器,使用标记-整理算法。
Parallel Old收集器
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法。
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。基于标记-清除算法实现,整个过程分为4个步骤:
- 初始标记(CMS initial mark)
- 并发标记(CMS concurrent mark)
- 重新标记(CMS remark)
- 并发清除
初始标记。重新标记这连个步骤需要“Stop the word”,初始标记仅仅只是标记一下GC Root能直接关联到的对象,速度很快;并发标记是从GC Roots的直接关联对象开始遍历整个对象图,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行;而重新标记阶段是为了修正并发标记期间,因用户线程继续运作导致标记产生的一些变动的那一部分对象的标记记录,这个时间段的停顿时间通常比初始标记记录稍长一些,但也远比并发标记阶段的时间短,最后清理阶段,清理标记阶段判断已死亡的对象,由于不需要移动存活对象没所以这个阶段可以与用户线程同时并发执行。
CMS的有点在于并发收集和低停顿。但是至少下面3个缺点:
- CMS收集器对处理器资源非常敏感,并发阶段虽然不会导致用户线程停顿,但却会占用一部分线程(或者处理器的计算能力)而导致应用程序变慢,降低吞吐量。CMS默认启动的回收线程数是(处理器核心线程数+3)/4,随着处理器核心线程数的增加而下降。
- 无法处理浮动垃圾。浮动垃圾指的是一部分的垃圾出现在标记过程结束,CMS无法当次收集中处理掉的,只能下次垃圾收集时在清理掉的垃圾。无法处理浮动垃圾可等导致“Concurrent Mode Failure”失败进而导致一次完全的“Stop the word”的Full GC的产生。由于垃圾收集阶段用户线程还在执行,必须预留一部分的空间提供给用户线程使用,所以不能像其它收集器一样待到老年代几乎完全填满再进行收集。JDK5默认值是68%,也可通过-XX:CMSInitiatingOccu-pancyFraction设置。
- 因为基于标记清除算法会产生大量的空间碎片,为了解决这个问题,CMS提供参数-XX:CMSFullGCsBeforeCompactAtFullCollection设置(默认开启),用在进行Full GC的时候开启内存碎片的合并整理过程。
Garbage First收集器
Garbage First(简称G1)收集器可以面向堆内任何部分来组成回收集进行回收,衡量标准不再是那个分代,而是哪块内存中存放的垃圾数量最多,回收效益最大,这就是G1收集器的Mixed GC模式。G1不在坚持固定大小以及固定数量的分代区域划分,而是把连续的java堆划分为多个大小相等的独立区域(Region),每个region都可以根据需要,扮演新生代的Eden空间、Survivor空间或者老年代空间。Region还有一种特殊的Humongous区域,专门用来存储大对象,G1认为只要大小超过一个Region容量一般的对象即可判定为大对象。每个Region的大小可通过参数-XX:G1HeapRegionSize设定,范围1MB~32MB,且应为2的N次幂。对于超过整个Region的超级大对象,将会被存放在N个连续的Humongous Region之中。G1大多数行为把Humonous Region当做老年代看待。
G1之所以能够建立预测的停顿时间模型,是因为它将Region作为单词回收的最小单元,即每次收集到的内存空间大小Region的整数倍,这样可以有计划的避免在整个Java堆中进行全区域的垃圾收集。更具体的处理思路是让G1收集器去跟踪各个Region里面的垃圾堆积的“价值”大小,价值即回收所获得的空间大小和回收所需时间的经验,然后维护一个优先级列表,每次根据用户设定的停顿时间(-XX:MaxGCPauseMillis指定)优先处理回收价值受益最大的Region。
G1对记忆集的处理,每个Region都维护自己的记忆集,这些记忆集会记下别的Region指向自己的指针并标记这些指针分别在那些卡页范围之内。G1的记忆集在存储结构的本质上是一种哈希表,Key是别的Region得起始地址,value是一个集合,里面的存储结构是卡表的索引号。这种双向的卡表结构(卡表记录的我指向谁,这种结果还记录了谁指向我)更加复杂。由于Region的数量多,也有更高的内存负担。
G1收集器是通过原始快照(SATB)的方式处理并发标记阶段收集线程和用户线程并行处理的问题。此外G1为每个Region设计了两个名为TAMS(Top At Mark Start)指针,把Region中的一部分空间划分出来用于并发回收过程中的行的对象的分配,并且回收时新分配的对象地址都要在这两个指针位置以上。G1收集器默认这个地址上对象是被隐性标记过得,即默认他们是存活的,不纳入回收范围。
G1收集器的停顿预测模型是以衰减均值为理论基础实现的,在垃圾收集的过程中,G1收集器会记录每个Region回收耗时、每个Region记忆卡里脏卡数量等各个可测量的步骤花费的成本,并分析得出平均值、标准偏差、置信度等统计信息。这里强调的“衰减平均值”是指它会比普通的平均值更容易受到新数据的影响。平均值代表整体的平均状态、但衰减平均值更准确的代表最近的平均状态。
G1收集器过程大致可划分为下面四个步骤:
- 初始标记 仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS的指针的值,这个阶段需要停顿线程,但耗时很短,而且是借用Minor GC的时候同步完成的,所以G1在这个阶段并没有额外的停顿。
- 并发标记 从GC Roots开始对堆中对象进行可达性分析,这个阶段耗时很长,但可与用户线程并发执行,当对图像扫描完成之后,还要重新处理SATB记录下来的并发时引用变动问题。
- 最终标记 对用户线程做一个短暂的暂停,用于处理并发阶段后仍遗留下来的最后那少部分STAB记录。
- 筛选回收 负责更新Region的统计数据,对各个Region的价值进行排序,根据用户的停顿时间制定回收计划。可以选择任意多的Region构成回收集,然后把决定回收的Region中的存活对象复制到空的Region中,再清理掉整个旧的Region的全部空间。因为涉及到活的对象的移动,必需暂停用户线程,有多条收集器线程同步完成。