一、新生代收集器
Serial收集器(单线程+复制算法)
Serial收集器为单线程收集器,当其GC线程执行清理工作时,用户线程必须全部停止。由于桌面应用一般分配给JVM的内存不是很大,停顿时间一般在几十毫秒不会给用户造成太大影响,因此可以用于用户(Client)模式的虚拟机下。
ParNew收集器(多线程+复制算法)
ParNew(Parallel New)收集器与Serial收集器最大的区别就是为多线程垃圾收集(多条GC线程是并行执行的!),但是仍需要暂停用户线程。该收集器为运行在Server模式下服务器首选的新生代垃圾收集器,因为CMS收集器可以和Serial或ParNew配合使用。
Parallel Scavenge收集器(多线程+复制算法+吞吐量)
Parallel Scavenge收集器最大的不同是别的收集器关系的是如何将停止用户线程的时间做到最低,而Parallel Scavenge关心的是如何达到一个可控制的吞吐量。
吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间)
(1)停顿时间越短越适合需要与用户交互的程序,良好的响应速度能提升用户体验。另外GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的:系统把新生代调小一些,收集300MB新生代肯定比收集500MB快吧,这也直接导致垃圾收集发生得更频繁一些,原来10秒收集一次、每次停顿100毫秒,现在变 成5秒收集一次、每次停顿70毫秒。停顿时间的确在下降,但吞吐量也降下来了;(2)高吞吐量可以高效的利用CPU时间,尽快的完成运算任务,主要适合在后台运算而不需要太多交互的任务。
Parallel Scavenge收集器与ParNew的区别还在于Parallel Scavenge收集器为自适应收集器。当把-XX:+UseAdaptiveSizePolicy这个参数打开之后,就不需要 手工指定新生代的大小(-Xmn)、Eden与 Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。
注意CMS收集器只能与Serial与ParNew配合工作。
二、老年代收集器
Serial Old收集器(单线程+标记整理)
Serial Old收集器为Serial收集器的老年代版本,其应用场景与Serial一致。另外还可以用作CMS收集器发生Concurrent Mode Failure失败时使用。
Parallel Old收集器(多线程+标记整理)
在jdk1.6之前Parallel Scavenge收集器是很尴尬的,原因就是当时CMS收集器只能与Serial与ParNew配合工作。所以其只能配合Serial Old工作。由于Serial Old为单线程收集器(多CPU无法充分发挥性能),严重拖累了Parallel Scavenge收集器的效率。所以以后出现了Parallel Old收集器与Parallel Scavenge收集器配合使用。Parallel Scavenge收集器与Parallel Old收集器的配合使用可以在注重吞吐和CPU资源的敏感场合使用。
CMS收集器(多线程+标记清理)
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,该收集器应用在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS有四个阶段:
1)初始标记(stop the world)
将GC Root能直接关联的对象进行标记。由于只标记的直接关联的对象,所以stw的时间很短,追溯GC Root的工作放到并 发标记执行。注意此阶段为单线程!
2)并发标记(时间很长)
进行GC Root追溯,对GC Root间接引用的对象进行枚举。这部分时间会很长且是和用户线程并发执行,所以用户的线程很 可能将GC Root的引用关系进行修改。
3)并发预清理
由于CMS目的是减少stw时间,所以重新标记阶段要尽可能短。因此在此阶段标记从新生代晋升的对象、新分配到老年代的 对象以及在并发阶段被修改了的对象。
CMS通过GC Root扫描新生代和老年代来确认老年代存活的对象。
老年代的机制与一个叫CARD TABLE的东西(这个东西其实就是个数组,数组中每个位置存的是一个byte)密不可分。CMS将老年代的空间分成大小为512bytes的块,card table中的每个元素对应着一个块。并发标记时,如果某个对象的引用发生了变化,就标记该对象所在的块为dirty card。并发预清理阶段就会重新扫描该块,将该对象引用的对象标识为可达。
并发标记时对象的状态:
但随后current obj的引用发生了变化,将该块标记为dirty:
在并发预清理阶段进行修改:
结束预清理阶段
4)重新标记(stop the world)
暂停所有用户线程,重新扫描堆中的对象,进行可达性分析,标记活着的对象。有了前面的基础,这个阶段的工作量被大大减 轻,停顿时间因此也会减少。注意这个阶段是多线程的。
5)并发清理(时间很长)
用户线程被重新激活,同时清理那些无效的对象。
CMS缺点有三个:
1)CMS收集器对CPU资源敏感
CMS比较占用CPU资源,总吞吐量会降低。CMS默认启动的回收线程数是(CPU数量+3)/4,也就是当CPU在4个以上时占用 不少于25%的CPU资源,资源占用随着CPU数量的增加而减少。
2)CMS无法处理并发清理阶段中产生的垃圾
由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现 在标记过 程之后,CMS无法在当次收集中处理掉 它们,只好留待下一次GC时再清理掉。这一部分 垃圾就称为"浮动垃圾"。也是由于 在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足 够的内存空间给用户线程使用,因此CMS收集器不能像其他 收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。当空间不够时 就会使用Serial Old收集器进行Full GC。
3)CMS基于标记-清除算法的缘故导致碎片产生
由于标记-清除算法并不会进行整理,所以会导致内存碎片产生进而导致分配对象失败触发FullGC。