垃圾收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现。下图展示了7种不同分代的收集器,如果两个收集器之间存在连线,就说明他们可以搭配使用。并没有最好的收集器这一说,我们需要选择的是对具体应用最合适的收集器。
1)Serial垃圾收集器(单线程、复制算法)--新生代
是最基本垃圾收集器,使用复制算法,曾经是JDK1.3.1之前新生代唯一的垃圾收集器。Serial是一个单线程的收集器,它不但只会使用一个CPU或一条线程去完成垃圾收集工作,并且在进行垃圾收集的同时,必须暂停其他所有的工作线程,直到垃圾收集结束。Serial垃圾收集器虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于限定单个CPU环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率,因此Serial垃圾收集器依然是java虚拟机运行在Client模式下默认的新生代垃圾收集器。
2)ParNew垃圾收集器(Serial+多线程)--新生代
ParNew收集器其实是Serial收集器的多线程版本,它是许多运行在Server模式下的虚拟机中首选的新生代收集器,因为除了Serial收集器外,目前只有它能与CMS收集器配合工作,因此是很多java虚拟机运行在Server模式下新生代的默认垃圾收集器。
ParNew垃圾收集器除了使用多线程进行垃圾收集之外,其余的行为和Serial收集器完全一样,ParNew垃圾收集器在垃圾收集过程中同样也要暂停所有其他的工作线程。
ParNew收集器默认开启和CPU数目相同的线程数,可以通过-XX:ParallelGCThreads参数来限制垃圾收集器的线程数。【Parallel:平行的】
3)Parallel Scavenge 收集器(多线程复制算法、 高效)--新生代
Parallel Scavenge 收集器也是一个新生代垃圾收集器, 同样使用复制算法, 也是一个多线程的垃 圾收集器, 它重点关注的是程序达到一个可控制的吞吐量(Thoughput, CPU 用于运行用户代码 的时间/CPU 总消耗时间, 即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)) , 高吞吐量可以最高效率地利用 CPU 时间, 尽快地完成程序的运算任务, 主要适用于在后台运算而 不需要太多交互的任务。 自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个 重要区别。
4)CMS收集器(多线程标记清除算法)
它是一种以获取最短回收停顿时间为目标的收集器。优点:并发收集,低停顿。基于“标记-清除”算法。目前很大一部分Java应用都集中在互联网站或B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验,CMS收集器就非常符合这类应用的需求。运作过程较复杂,分为4个步骤:
缺点:
对CPU资源非常敏感,面向并发设计的程序都会对CPU资源较敏感。CMS默认的回收线程数: (CPU数量+3)/4
无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。并发清理阶段用户程序运行产生的垃圾过了标记阶段所以无法在本次收集中清理掉,称为浮动垃圾。CMS收集器默认在老年代使用了68%的空间后被激活。若老年代增长的不是很快,可以适当调高参数-XX:CMSInitiatingOccupancyFraction 提高触发百分比,但调得太高会容易导致“Concurrent Mode Failure”失败。
基于“标记-清除”算法会产生大量空间碎片。提供开关参数-XX:+UseCMSCompactAtFullCollection 用于在Full GC服务之后进行碎片整理过程,内存整理的过程是无法并发的。但是停顿时间会变长。
-XX:CMSFullGCsBeforeCompation 设置在执行多少次不压缩的Full GC后,跟来来一次带压缩的。
5、G1收集器(同时支持新生代和老年代)
Garbage first 垃圾收集器是目前垃圾收集器理论发展的最前沿成果,相比与CMS收集器,G1收集器两个最突出的改进是:
1.基于标记-整理算法,不产生内存碎片。
2.可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。
G1收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域
的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾
最多的区域。区域划分和优先级区域回收机制,确保G1收集器可以在有限时间获得最高的垃圾收
集效率。