写在前面:
基于JDK1.7Update14之后的HotSpot虚拟机(这个版本中正式提供了商用的GI收集器)
虚拟机包含的所有收集器如图所示:
说明:两个收集器之间存在连线,表示可以搭配使用。
1、Serial收集器
单线程收集器,“单线程”:只使用一个CPU或者一条收集线程去完成垃圾收集工作,并且在垃圾收集时会“stop the world”,即暂停其他所有工作线程,直到收集结束。
优势:简单高效(与其他收集器的单线程相比),对于限定单个CPU的环境来说,没有线程切换开销,可以获得最高的单线程切换效率。
是虚拟机运行在client模式下的默认新生代收集器。
2、ParNew收集器
Serial收集器的多线程版本;是许多运行在Server模式下的虚拟机中首选的新生代收集器(其中一个重要非性能原因是,除了Serial收集器,只有ParNew收集器能与CMS收集器配合工作)
可使用 -XX: ParallelGCThreads参数来限制垃圾收集的线程数;
3、Parallel Scavenge收集器
使用复制算法的新生代收集器,多线程并行;
特点(与ParNew的主要不同之处):
- 关注点与其他收集器不同,目标着重于达到一个可控制的吞吐量(CPU运行时间/CPU总消耗时间)而不是尽可能缩短垃圾收集时用户的停顿时间,可以高效率地利用CPU时间,主要适合在后台运算而不需要太多交互的任务;也经常称为“吞吐量优先”收集器
-
自适应调节策略。 -XX:+UseAdaptiveSizePolicy 一个开关参数,打开后虚拟机会根据当前系统运行情况自行优化/动态调整新生代大小(-Xmn)、Eden与Survivor区的比例(-XX: SurvivorRatio)、晋升老年代对象年龄(-XX: PretenreSize Threshold)等参数。
提供了两个参数来控制吞吐量: 最大垃圾收集停顿时间-XX: MaxGCPauseMillis 吞吐量大小设置: -XX:GCTimeRatio
其中,MaxGCPauseMillis最大垃圾收集停顿时间是一个大于0的毫秒数,收集器将尽可能保证内存回收时间少于这个数值。但并不是参数值设置得越小,垃圾收集速度越快。GC停顿时间缩短是以牺牲吞吐量和新生代空间为代价的。比如系统把新生代空间调小一些,垃圾收集频繁一些,这些都会导致停顿时间下降,但同时页降低了吞吐量。
4、Serial Old收集器
Serial收集器的老年代版本(见图1)
主要用于Client模式下的虚拟机;
Server模式下的两点主要用途:在JDK1.5以及之前的版本中与Parallel Scavenge收集器搭配使用;作为CMS收集器的后备方案,在并发收集发生失败(Concurrent Mode Failure)时使用;
5、Parallel Old收集器
Parallel Scavenge的老年代版本;使用多线程和标记整理算法;JDK1.6版本中开始提供,在此之前新生代如果选择了Parallel Scavenge版本,老年代只能选用Serial Old版本,因为其无法与CMS收集器搭配使用;
6、CMS(Concurrent Mark Sweep)收集器
收集器目标:获取最短回收停顿时间;也称为并发低停顿收集器;
运作过程:初始标记(标记GC Roots能直接关联到的对象),并发标记(GC Roots Tracing),重新标记(修正并发标记期间因用户程序继续执行而导致产生变动的标记记录);并发清除。其中,初始标记、重新标记两个步骤需要stop the world。
缺点:1.对CPU资源非常敏感(并发情况下,可能导致用户程序的执行速度下降,让人无法接受),改进:增量式并发收集器(让GC线程、用户线程交替执行,但实践效果一般,已不提倡使用);2.无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”而导致另一次Full GC的产生;浮动垃圾:并发清理阶段用户程序产生的新垃圾;另外,由于垃圾收集阶段用户线程还需要运行,也就需要预留足够的内存空间给用户线程使用,因此CMS收集器不能等到老年代将近填满了之后进行垃圾收集,需要预留一部分空间;参数-XX:CMSInitiatingOccupancyFraction 触发百分比(老年代使用了这个比率的空间后就会触发CMS收集器进行垃圾收集),该参数太低内存回收次数会提高;太高会容易导致大量的“Concurrent Mode Failure”,性能反而降低;如果CMS运行期间预留的内存无法满足内存需要,就会产生“Concurrent Mode Failure”,这时会临时启用后备方案Serial Old收集器来重新进行老年代的垃圾收集。3.标记-清除算法容易产生大量内存碎片;解决方案: -XX: +UseCMSCompactAtFullCollection(默认开启)CMS收集器顶不住要进行Full GC时开启内存碎片的合并整理,内存整理过程无法并发,会导致停顿时间延长;参数-XX: CMSFullGCsBeforeCompaction,用于设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认值为0,表示每次进入Full GC时都要执行碎片整理)
7、G1收集器(Garbage-First)
面向服务端应用的垃圾收集器
运作过程:初始标记(标记GC Roots能直接关联到的对象)、并发标记(从GC Root开始对堆中对象进行可达性分析)、最终标记(修正变动标记)、筛选回收(根据用户期望停顿时间制定回收计划,筛选回收部分Region);
与其他GC收集器相比,G1具备如下特点:
1.并行与并发(见附解释):能充分利用多CPU、多核环境下的硬件优势,使用多CPU/多核来缩短stop the world停顿时间,其它收集器需要暂停用户线程执行的GC动作,G1可以与用户程序并发执行;
2.分代收集:保留了分代概念,采用不同的方式去处理新建的对象、已经存活了一段时间、熬过多次GC的旧对象,从而获取更好的收集效果;
3、空间整合:整体上基于“标记-整理”算法,局部上基于“复制”算法,这两种算法意味着G1运作期间不会产生内存碎片,收集后能提供规整的可用内存;该特性有利于程序的长期运行,不会产生大对象找不到空间分配而导致提前触发下一次GC;
4、可预测的停顿:除了追求停顿外,还能建立可预测的停顿时间模型,让使用者明确制定在M毫秒的时间片段内,消耗在垃圾收集上的时间不超过N毫秒,这几乎已经是实时Java的垃圾收集器的特征了;
另外,G1使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内获取了尽可能高的收集效率;
垃圾收集器参数总结:
- UseSerialGC :虚拟机运行在client模式下的默认值,打开此开关后,使用Serial+Serial Old的收集器组合进行内存回收;
- UseParNewGC:打开此开关后,使用ParNew+Serial Old的收集器组合进行内存回收;
- UseConMarkSweepGC:打开此开关后使用CMS+ParNew+Serial Old的组合进行内存回收,其中Serial Old为CMS出现Concurrent Mode Failure失败时的的后备服务器;
- UseParallel GC:运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge + Serial Old(PS MarkSweep)的收集器组合回收内存;
- UseParallelOldGC: 打开此开关后,使用Parallel Scavenge+Parallel Old的收集器组合回收内存;
- Survivor Ratio:新生代中Eden区与Survivor区的容量比值,默认为8;
- PretenureSizeThreshold: 直接晋升到老年代对象大小;设置这个参数后,大于该参数的对象将直接在老年代分配;
- MaxTenuringThreshold:晋升到老年代的对象年龄;每个对象在坚持一次MInor GC后,年龄加1,当年龄超过这个参数时就进入老年代;
- UseAdaptivePolicy:动态调整Java堆中各个区域的大小以及进入老年代的年龄;
- HandlePromotionFailure:是否运行分配担保失败,即老年代的剩余空间不足以应对新生代整个Eden区和Survivor区所有对象都存活的极端情况;
- ParallelGCThreads: 并行GC时进行内存回收的线程数量;
- GCTimeRatio:GC时间占总时间的比率,默认为99;即运行1%的GC时间。仅在使用Parallel Scavenge收集器时生效;
- MaxGCPauseMillis:GC最大停顿时间;仅在使用Parallel Scavenge收集器时生效;
- CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间使用多少时触发垃圾收集,默认值为68%,仅在使用CMS收集器时生效;
- UseCMSCompactAtFullCollection:设置CMS在完成垃圾收集后是否进行内存碎片整理,仅在使用CMS收集器时生效;
- CMSFullGCsBeforeCompaction: 设置CMS收集器在进行若干次垃圾收集后再启动内存碎片整理,仅在使用CMS收集器时生效;
读者若有疑问欢迎留言一起交流,共同学习成长
参考
深入理解Java虚拟机