1、Serial收集器
一个单线程收集器,只使用一条线程完成垃圾收集工作,必须暂停其他工作线程(Stop The World)直到收集工作完成。
运行在client模式下的虚拟机默认的新生代收集器,对于限定单个CPU的环境来说,Serial收集器没有线程交互的开销,可以获得最高的单线程收集效率。在桌面应用场景中,分配给运行在client模式下虚拟机的内存至多一两百兆,停顿时间最多一百多毫秒,只要不频繁发生,应用是可以接受的。
2、ParNew收集器
Serial收集器的多线程版本,默认开启的收集线程数与CPU数量相同,其余行为(控制参数、收集算法、Stop The World、对象分配规则、回收策略等)与Serial收集器完全一样。
运行在server模式下的虚拟机首选的新生代收集器 ,当老年代选择CMS收集器时,新生代只能选择Serial或ParNew收集器(默认)中的一个。ParNew收集器在单CPU环境中性能一定不如Serial收集器,甚至由于线程交互的开销,在双CPU环境中性能都未必超过Serial收集器。
3、Parallel Scavenge收集器
并行的多线程新生代收集器,其他收集器的关注点在于尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的关注点在于提高吞吐量(CPU运行用户代码的时间与CPU总运行时间的比值,虚拟机总共运行100s,垃圾收集消耗1s,则吞吐量为99%)。
停顿时间越短越适合与用户交互的程序,良好的响应速度能提升用户体验。而高吞吐量则适合在后台运算不需要太多交互的程序,能够充分利用CPU时间完成运算任务。
Parallel Scavenge收集器提供了-XX: MaxGCPauseMillis(值N为大于零的毫秒数,代表最大垃圾收集停顿时间)和-XX: GCTimeRatio(值N为大于0小于100的整数,1/(1+N)代表垃圾收集时间占总时间的比率,默认为99,即允许最大1/(1+99)的垃圾收集时间)两个参数用于精确控制吞吐量。缩短停顿时间是以牺牲吞吐量和新生代空间为代价的,如系统原来的新生代空间为500M,10s收集一次,一次停顿100ms,则吞吐量为99%,现在参数的值改为70,则系统调整新生代空间为300M,5s收集一次,吞吐量为98.6%。
-XX:+UseAdaptiveSizePolicy参数用于启用/关闭虚拟机的自适应调节策略,开启之后,只需要把基本的内存数据设置好(如最大堆 -Xmx),再使用-XX: MaxGCPauseMillis或-XX: GCTimeRatio参数给虚拟机设置一个优化目标,那么具体细节参数的调节工作就有虚拟机完成了。虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整细节参数(新生代大小-Xmn、Eden与Survivor区的比率-XX:SurvivorRatio、晋升老年代对象年龄-XX:PretenureSizeThreshold等)以提供最适合的停顿时间和最大的吞吐量。
4、Serial Old收集器
Serial收集器的老年代版本,也是单线程收集器,使用标记-整理算法,主要用于client模式下的虚拟机。用于server模式下的虚拟机中主要有两大用途,一是在jdk1.5之前的版本中与Parallel Scavenge收集器搭配使用,一是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。
5、Parallel Old收集器
Parallel Scavenge收集器的老年代版本,在注重吞吐量及CPU资源敏感的场合,可以优先考虑年轻代使用Parallel Scavenge收集器加老年代使用Parallel Old收集器的组合。
6、CMS收集器
Concurrent Mark Sweep收集器以获取最短回收停顿时间为目标,基于标记-清楚算法实现。符合那些希望系统停顿时间短、重视服务响应速度的java应用的需求。
初始标记、重新标记这两个步骤任然需要Stop The World, 初始标记只标记GC Roots直接关联的对象,速度很快,并发标记是进行GC RootsTracing的过程。重新标记是为了修正并发标记期间用户程序运行导致标记产生变动的那一部分对象标记记录,这个步骤的停顿时间稍长于初始标记的停顿时间,远短于并发标记的停顿时间。整个过程中耗时最长的并发标记和并发清除过程,收集器线程都可以与用户线程一起工作,所以总体上来说CMS收集器的内存回收过程是与用户线程并发执行的。
缺点:
并发阶段,GC线程会占用CPU资源导致应用程序变慢,吞吐量降低;
由于并发清理阶段用户程序还在运行并产生新的垃圾,这部分垃圾(浮动垃圾Floating Garbage)出现在标记过程之后,CMS只能在下一次收集中清理掉它们。也是由于并发,CMS不能像其他收集器一样等到老年代快被填满才进行收集,需要预留一部分空间供并发收集时的用户程序使用,如果CMS运行期间预留的内存无法满足程序需要,会出现Concurrent Mode Failure,此时虚拟机将启动后备预案,启用Serial Old收集器重新进行老年代收集,导致停顿时间边长。参数-XX:CMSInitiatingOccupancyFraction用来设置老年代空间使用多少百分比时激活CMS收集器。
采用标记-清除算法导致大量空间碎片,无法分配足够的连续空间给大对象时提前触发下一次Full GC。开关参数-XX:+UseCMSCompactAtFullCollection(默认开启)用于在CMS顶不住要Full GC时开启内存碎片的合并整理过程,该过程无法并发,导致停顿时间边长。参数-XX:CMSFullGCsBeforeCompaction(默认值为0,表示每次Full GC都进行碎片整理)用于设置执行多少次不压缩的Full GC后,执行一次带压缩的Full GC。
7、G1收集器
Garbage First是一款面向服务端应用的垃圾收集器,使命是未来可以替换掉CMS收集器。具有如下特点:并行与并发、分代收集、空间整合(标记-整理算法)、可预测的停顿。
G1之前的收集器收集的范围都是整个新生代或者老年代,而G1是将整个java堆划分为多个大小相等的独立区域(Region),新生代和老年代不再是物理隔离,都是一部分区域(可以不连续)的集合。G1针对各区域进行收集并跟踪各区域垃圾堆积的价值(回收空间大小和回收所需时间)在后台维护一个优先列表,每次根据允许的收集时间优先回收价值最大的区域,从而保证在有限时间内获取尽可能高的收集效率。
G1收集器区域之间的对象引用和其他收集器新生代和老年代之间的对象引用,虚拟机使用Remembered Set来避免全堆扫描。每个区域维护一个各自的Remembered Set,虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同区域之中,如果是便通过CardTable把相关引用信息记录到被引用对象所属区域的Remembered Set中。回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。
初始标记阶段需要停顿线程,但用时很短,只标记GC Roots直接关联的对象,并修改TAMS(Next Top At Mark Start) 使并发标记阶段时用户程序能在正确可用的区域中创建新对象;并发标记阶段从GC Root开始分析堆中可达性对象 ,耗时较长,但可与用户程序并发执行;最终标记阶段需要停顿线程,但是可以并行执行,修正因并发标记阶段用户程序执行导致标记变动的那一部分标记,并通过Remembered Set Logs合并到Remembered Set中;筛选回收阶段对各个区域的回收价值和成本进行排序,根据用户期望的GC停顿时间制定回收计划,这个阶段其实也可以与用户程序并发,但因为只回收部分区域,时间是用户可控制的,且停顿用户线程将大幅提高收集效率。