如果说垃圾收集算法是Java虚拟机的指导思想,那么下面的垃圾收集器正是这些算法的实现,保证了jvm之运行时内存的正确使用和定时清理
一、新生代收集器
1、Serial
复制算法。
Serial收集器是最基本的收集器。
- 单线程
- stop the world
简单高效,没有线程交互的开销,适用于新生代的垃圾收集
2、ParNew
复制算法。
ParNew其实就是Serial收集器的多线程版本
- 多线程
- Stop the world
-XX:+UseConcMarkSweepGC使用CMS并发收集器的默认新生代收集器就是ParNew,也可以使用-XX:UseParNewGc强制指定
3、Parallel Scavenge
复制算法。
并行多线程收集器,它的关注点在于提高用户程序的吞吐量,吞吐量就是JVM运行用户程序代码的时间 / (用户程序代码时间 + 垃圾回收时间)。
JVM提供2个参数用于精确控制吞吐量:
* -XX:MaxGCPauseMillis
* -XX:GCTimeRatio
MaxGCPauseMillis垃圾收集最大停顿的时间,毫秒为单位。如果设置太小则会引起GC的次数变多
GCTimeRation配置垃圾收集的时间占比,相当于吞吐量的倒数。如果参数设置19,那么允许最大GC的时间占总时间的5%(也就是1/(1+19))。默认值为99,也就是允许最大1%的垃圾收集时间(1 / ( 1 + 99 ))
智能参数:-XX:+UseAdaptiveSizePolicy
如果启用这个参数,就不需要手工指定新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRatio)、晋升到老年代对象年龄(-XX:PretenureSizeThreshold) 等细节,JVM会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。
二、老年代收集器
1、CMS收集器
CMS(Concurrent Mark Sweep) 收集器是一种以获取最短回收停顿时间为目标的收集器,希望在发生收集时系统停顿的时间最短。
整个过程分为4步:
- 初始标记
- 并发标记
- 重新标记
- 并发清除
初始标记,重新标记需要“Stop the world”
- 初始标记仅标记GC Roots,速度很快
- 并发标记GC Roots的追踪
- 重新标记修正并发标记期间用户继续运作而导致标记产生变动的那一部分对象(这个阶段停顿时间比初始标记长,但比并发标记短)
CMS的缺点
占用一部分线程,或者CPU资源,影响系统吞吐量。默认:(cpu数量+3)/4,如果CPU为1核或者2核时,采用CMS会对用户程序产生影响
无法清除浮动垃圾。并发清理阶段,用户线程还继续运行,这时会继续产生垃圾,CMS无法回收这部分的垃圾,只能留到下一次,这种叫做浮动垃圾。
标记-清除算法,意味着垃圾收集会出现内存碎片。
针对CMS的缺点JVM做的努力
CMS占用用户线程,使得用户程序的吞吐量降低。为解决这个问题,虚拟机提供“增量式并发收集器”,是并发标记、并发清理的GC线程和用户线程抢占式交替执行 但是这种方案效果不明显,废弃了
CMS垃圾收集时,由于用户线程还继续运行,所以JVM必须要留足够的内存空间给用户线程使用,不能像其他收集器一样等老年代几乎被填满了再收集。JDK5默认内存空间使用到68%时会被激活,可以通过参数调节-XX:CMSInitiatingOccupancyFraction,JDK6默认92%。如果CMS运行期间,预留的内存无法满足程序的需要,会出现‘Concurrent Model Failure’失败,这时启动Serial Old收集器收集内存垃圾
CMS会产生内存碎片,如果剩余的内存中无法分配给足够大的对象,会提前触发Full GC,可以通过-XX:+UseCMSCompactAtFullCollection配置,当JVM顶不住要进行Full GC时开启内存碎片整理,但是GC的时间变长了。0表示Full GC时都进行内存碎片整理
2、Serial Old收集器
“标记-整理”算法
- 单线程
- 可以作为CMS收集的后备预案
就是当发生“Concurrent Model Fail”,也就是在垃圾收集过程中,如果剩余的内存空间无法分配给用户程序使用,则会启动该手机算法进行老年代的垃圾收集
3、Parallel Old收集器
“标记-整理”算法
- 多线程
配合新生代的Parallel Scavenge收集器,因为新生代的Parallel Scavenge只能选择Serial Old不能选择CMS,导致收集性能地,所以出现Parallel Old地老年代采用多线程的回收