一、Serial收集器
即串行收集器,该收集器是一个单线程工作的收集器,它在进行垃圾收集时,必须暂停其他所有的工作线程,直到垃圾收集完毕。“Stop The World”是由虚拟机在后台自动发起和结束的,在用户不可知、不可控的情况下停止正常的工作线程,对很多应用程序而言是不可接受的。
Serial收集器在新生代中运用,采取复制算法暂停所有用户线程,与其对应的是Serial Old收集器,应用于老年代,采用标记-整理算法暂停所有用户线程。
Serial收集器相对于其他收集器而言,更为简单高效,是所有收集器里二外内存消耗最小的。
选择Serial收集器参数:
-XX: +UseSerialGC
二、ParNew收集器
ParNew收集器的本质是Serial收集器的多线程并行版本,除了使用多条线程进行垃圾回收外,其余的都与Serial收集器完全一致,包括Serial收集器的控制参数、收集算法、对象分配规则等。
ParNew收集器有一个特点,如果选用CMS收集器后,新生代默认选用的就是ParNew收集器,也是除了Serial收集器以外唯一能与CMS收集器配合工作的收集器。
选择ParNew收集器参数:
-XX: +UseParNewGC
注:JDK9之后取消了这一控制参数,选用CMS默认的新生代收集器为ParNew,也就是ParNew彻底并入了CMS。
三、Parallel Scavenge收集器
即常说的“吞吐量优先收集器”,Parallel Scavenge收集器是一款新生代收集器,是基于标记-复制算法实现的,Parallel Scavenge收集器与ParNew非常像,它更关注吞吐量(Throughput)这一指标。
高吞吐量可以最高效率的利用处理器资源,吞吐量高,就意味着垃圾收集时间短,而更多的精力投入到程序的主要运算任务上。
Paraller Scavenge 控制参数:
-XX: +UseParallelGC
-XX: GCTimeRatio(吞吐量大小,是垃圾收集时间占总时间的百分比)
-XX: MaxGCPauseMillis(最大垃圾收集停顿时间,收集器尽力保证内存回收花费的时间不超过设定的值)
Parallel Scavenge还具有自适应的调节策略,通过-XX: +UseAdaptiveSizePolicy 参数控制,当该参数激活后,将不需要人工指定新生代(-Xmn)、Eden与Survivor的比例(-XX: SurvivorRatio),虚拟机会根据当前系统的运行情况收集性能监控信息,动态的调整这些参数以提供最合适的停顿时间或者最大吞吐量。
四、Serial Old收集器
Serial Old收集器和其名字一样,是Serial收集器的老年代版本,同样是一个单线程的收集器,使用标记-整理算法。
选择Serial Old收集器参数:
-XX: +UseSerialOldGC
五、Parallel Old 收集器
Parallel Old收集器时Parallel Scavenge收集器的老年代版本,基于标记-整理算法。
选择 Parallel Old收集器参数:
-XX: +UseParallelOldGC(老年代)
六、 CMS收集器
CMS收集器全称Concurrent Mark Sweep ,是一种以获取最短回收停顿时间为目标的收集器。使用CMS收集器的应用程序更多的是关注服务的响应速度,希望停顿时间尽可能的短,从而带来更好的用户体验。
CMS收集器是基于标记-清除算法实现的,运作过程分为四步:
其中,初始标记和重新标记这两个步骤仍然需要暂停用户线程。
(1)初始标记:仅仅只标记一下GC ROOTS直接关联的对象,速度很快。
(2)并发标记:从GC ROOTS的直接关联对象开始遍历整个对象图,耗时虽然长,但是不需要停顿用户线程。
(3)重新标记:其目的是为了修正并发标记期间,因用户程序继续运行而导致标记变动的那一部分对象。
(4)并发清除:清理标记阶段判定为死亡的对象。
CMS使用面广,最主要的优点在于,在最耗时的并发标记与并发清除阶段,CMS无需暂停用户线程,CMS收集器的内存回收过程是可以与用户线程一起并发执行的。
CMS的优点也为CMS带来了部分负面的影响,由于CMS并发标记和并发清理阶段是和用户线程同时进行的,程序难免会在运行中产生新的垃圾对象,如果这部分的“浮动垃圾”产生在并发标记结束后,那么CMS无法在当次收集中处理掉。
CMS面临的另一问题是,并发标记虽然不会停止用户线程,但是会占用一部分CPU资源从而导致应用程序变慢,CMS默认启动的回收线程数是 (处理器核心数量+3)/ 4 。当处理器核心数量不足4个时,CMS对程序的影响会变大。
由于CMS与用户线程同时运行的情况,CMS必须预留足够的内存空间给用户线程使用,因此CMS无法像其他垃圾回收器一样等着老年代几乎被填满时再收集。JDK5默认CMS收集器当老年代使用了68%的空间后被激活,而这一阈值在JDK6时提升到了92%,无论何种阈值,CMS都面临着“并发失败”的情况,即CMS预留空间不足以让程序运行产生的新对象的需要。当出现并发失败的情况时,虚拟机将采用预案:冻结用户线程,启用Serial Old收集器来重新进入老年代回收垃圾,Serial Old的特性造就了停顿时间更长的情况。
-XX: CMSInitiatingOccupancyFraction (调整CMS触发的百分比)
最后,标记-清除算法的特性,CMS收集结束后会产生打量的内存空间碎片,可能会导致大对象因无法找到连续的内存空间而引发一次Full GC。
七、G1收集器(Garbage First)
G1应该是垃圾回收器当中重要的里程碑,以往的垃圾回收器关注的范围要么是整个新生代,要么就是整个老年代,而G1可以面向堆内存的任何部分来组成回收集,衡量标准不再是分代,而是哪块内存的垃圾数量最多,回收收益最大。这就是G1收集器的Mixed GC 模式。
G1虽然保留了新生代和老年代的概念,但是不再坚持固定大小的分代区域划分,而是把堆划分为多个大小相等的独立区域,这些区域被称为Region,每一个Region根据需要,都可以扮演新生代的Eden、Survivor、或者老年代空间。收集器会根据扮演不同角色的Region采用不同策略去处理。Region中还有一类特殊的的Humongous区域,专门存放大对象,G1认为只要大小超过了Region容量一半的对象即为大对象。对于超过整个Region容量的超级大对象,将会被存放在N个Humongous Region区域中。
Region的大小可以通过参数控制,取值范围1MB~32MB:
-XX: G1HeapRegionSize
G1收集器将Region作为单次回收的最小单元,每次回收的内存空间都是Region大小的整数倍,G1会根据回收价值,维护一个优先级列表,优先回收价值收益最大的Region。
这里存在一个问题,Region当中可能会存在跨Region引用的对象,G1在这里同样采用了记忆集(挤一)的处理手段,每个Region都维护有自己的记忆集,这些记忆集会记录下别的Region指向自己的指针,并标记指针分别在哪些卡页范围内。简单理解就是,Region维护了一张Key为Region起始地址,Value为卡表的索引号的一张哈希表。
解决了跨Region引用对象的问题,那么接下来要保证的就是并发过程中,不能被用户线程改变对象的引用关系。G1为每一个Region设计了两个名为TAMS(Top at Mark Start)的指针,把Region中的一部分空间划分出来用于并发回收过程中,用户线程产生的新对象的分配,并发回收时新产生的对象地址都必须在这两个指针的位置以上,G1收集器默认在这个地址上的对象是不纳入回收范围的。
G1收集器的过程大致分为四步:
(1)初始标记:仅仅标记GC ROOTS直接关联的对象,短暂的停顿用户线程。
(2)并发标记:从GC ROOTS开始做可达性分析,递归扫描整个队里的对象,找出要回收的对象。
(3)最终标记:对用户线程做一次短暂停顿。用于处理并发阶段结束后仍遗留下来的少量的原始快照记录。
(4)筛选回收:负责更新Region的统计数据,对各个Region的回收价值和成本排序,根据用户期望的停顿时间制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧的Region空间。因涉及存活对象的移动,因此会暂停用户线程。