根据不同的应用,所产生的内存对象不同,要满足的业务场景也不一样,因此没有完美的垃圾收集器,需要根据实际情况去选择。垃圾收集器是垃圾收集算法的具体实现。
Java的堆内存根据对象的生命周期也会分为新生代和老年代。新生代的对象朝生夕死,回收率高,老年代的对象存活时间长,回收率低。 两者往往会使用不同的垃圾收集器。
新生代收集器
- Serial 收集器:单线程,简单而高效(与其他收集器的单线程比),Client模式下的首选
开启选项:-XX:+SerialGC
Stop The World
这个收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束
- ParNew收集器:Serial的多线程版本
开启选项:-XX:+UseParallelGC
ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器。
很重要的原因是:除了Serial收集器外,目前只有它能与CMS收集器配合工作。
- Parallel Scavenge收集器:吞吐量优先,适合后台运算多交互少的任务
吞吐量= 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)
老年代收集器
- Serial Old收集器:“标记-整理”,作为CMS收集失败后的后备方案
开启选项:-XX:+SerialGC
- Parallel Old收集器:多线程+“标记-整理”,吞吐量优先
开启选项:-XX:+UseParallelOldGC
- CMS收集器:“Concurrent Mark Sweep” 最短停顿时间,server端首选,“标记清除”
开启选项:-XX:+UseConcMarkSweepGC
初始标记(Stop The World)
并发标记
重新标记(Stop The World)
并发清除
初始标记和重新标记仍需暂停所有用户线程,即“Stop the World”。初始标记只是标记GC Roots能直接关联的对象,速度很快。并发标记阶段,用户线程与标记线程并发,就是进行GC Roots Tracing的过程。而重新标记则只是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。整个收集过程中耗时最久的并发标记和并发清除则和用户线程一起工作,所以总地来讲,CMS中GC线程是和用户线程一起并发执行的。其工作过程如图所示:
缺点:
i.对CPU资源非常敏感
ii.无法处理浮动垃圾
iii.有大量空间碎片产生(标记-清除算法)
G1收集器
开启选项:-XX:+UseG1GC
G1(Garbage First)收集器是当前收集器技术发展的最前沿成果,它与其他GC收集器相比,具有如下特点:
- 并行+并发。可充分利用CPU资源;
- 分代收集;
- G1从整体看是“标记-整理”算法,从局部(两个Region之间)看,是“复制”算法。 不会产生空间碎片;
- 可预测的停顿。建立可预测的态度时间模型,能让使用者明确指定在一个长度为M毫秒的时间内,消耗在垃圾收集的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。
G1之前的收集器是进行收集的范围是整个新生代或 老年代,而G1将整个Java堆(包括新生代、老年代)划分为多个大小固定的独立区域(Region)。G1跟踪这些Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据优先级从列表中挑选回收价值最大的Region进行回收(这也就是Garbage First名称的由来)。其工作过程类似于CMS,分为如下四个步骤:
i.初始标记
ii.并发标记
iii.最终标记
iv.筛选回收
前三个步骤与CMS类似,最后的筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。