JVM的几种垃圾收集器
Serial收集器
基本原理:
- 新生代收集器,采用标记复制算法
- 它是一个单线程收集器,只会使用一个CPU和一条收集线程完成GC。
缺陷:
- 在于它“Stop-The-World”的收集方式。进行垃圾收集时必须暂停其他线程的所有工作
优点:
- 简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做GC反而效率更高。
适用场景:
- 在用户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,GC停顿时间可接受。因此Serial收集器对于运行在Client模式下的虚拟机来说是一个很好的选择。
ParNew 收集器
基本原理:
- 新生代收集器,采用标记复制算法
- ParNew收集器其实就是Serial收集器的多线程版本,它使用了多条线程进行GC。其余行为包括Serial收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一样。且两者共用了相当多的代码。
优点:
- 随着CPU数量增加,它对于GC时系统资源的有效利用还是很有好处的。
适用场景:
- Server模式下首选的新生代收集器,其中一个重要原因是目前除Serial收集器外,只有它能与CMS收集器相配合。
Parallel Scavenge收集器
基本原理:
- 新生代收集器,采用复制算法
- 采用多线程收集
- 此收集器的重点在于达到一个可控的吞吐量
- 亦称为:吞吐量优先收集器
- 所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)。比如,JVM总运行时长100分钟,GC占1分钟,那么吞吐量就是99%。
优点:
- 具有自适应调节策略
- JVM具有一个参数可以打开此策略。打开后,JVM会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大吞吐量。
使用场景:
- 需要高吞吐量的场景:主要适用在后台运算而不需要太多交互的任务。
Serial Old收集器
基本原理:
- Serial Old 是 Serial收集器的老年代版本
- 单线程
- 使用“标记整理”算法
适用场景:
- 主要意义在于给Client模式下的JVM使用。
- 在Server下也有量大用途:
- 在JDK 1.5 之前的版本中与 Paroled Scavenge 收集器搭配使用
- 作为CMS收集器的后备预案,在并发收集失败下使用
Parallel Old 收集器
基本原理:
- Parallel Scavenge收集器的老年代版本
- 多线程处理
- 使用“标记整理”算法
适用场景:
- 对于注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge + Parallel Old收集器
CMS 收集器
基本原理:
- CMS = Concurrent Mark Sweep 并发标记清除
- 目标:获取最短GC停顿时间
- 青年老年代均有涉猎
- CMS中利用ParNew处理young代
- old代使用标记清除算法,并分步骤进行
运作过程:
- 初始标记
- CMS initial mark
- 需要 Stop-The-World
- 只是标记一下GC roots能直接关联到的对象,速度很快
- 并发标记
- CMS concurrent mark
- 进行GC RootsTracing的过程,也就是依照初始标记中发现的可达对象寻找其他的可达对象
重新标记
- CMS remark
- 需要 Stop-The-World
- 修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录
- 此阶段的停顿一般会比初始标记阶段稍长一些,但远比并发标记的时间短
并发清除
- CMS concurrent sweep
- 清理垃圾对象,这个阶段收集器线程和应用程序线程并发执行
- 初始标记
优点:
- 整个过程中耗时最长的并发标记和并发清除都可以与用户线程一起工作。总体上,CMS收集器的内存回收过程是与用户线程一起并发执行的。
缺点:
- 对CPU资源非常敏感:由于它占用了一部分线程而导致应用程序变慢 ,总吞吐量会降低。
- 当CPU在4个以上时,并发GC线程占用不少于25%CPU的资源,并随着CPU数量的增加而下降。
- 当CPU不足4个,如2个时,CMS对用户程序的影响就可能变的很大 。如果本来CPU负载就比较大,还分出一半去执行GC线程,就可能导致用户线程突然变慢。
- 无法处理浮动垃圾(Floating Garbage)
- 浮动垃圾:由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好等待下一次GC时再清理掉。这部分垃圾就称为“浮动垃圾”。
- 可能出现“Concurrent Mode Failure”失败
- CMS收集器不能像其他收集器那样等到老年代几乎完全填满再进行收集,而是需要预留一部分空间提供并发收集时程序的运作。如果预留空间不够,就会发生此失败。失败后会采用Serial Old进行老年代的收集,这样停顿时间会增长。
- 如果老年代增长不是太快,可以适当调高触发值(即老年代占用多少时会触发CMS),以降低回收次数从而获取更好的性能。
- CMS收集器不能像其他收集器那样等到老年代几乎完全填满再进行收集,而是需要预留一部分空间提供并发收集时程序的运作。如果预留空间不够,就会发生此失败。失败后会采用Serial Old进行老年代的收集,这样停顿时间会增长。
- 易产生碎片,进而导致FULL GC的方法
- Full GC : 也称为“Major GC”专指老年代垃圾回收行为
- 由于CMS采用 “标记-清除”算法,因此很容易造成碎片问题,进而无法找到足够空间分配对象,从而触发Full GC 。
- 解决:提供一默认开启的JVM参数,让JVM在CMS顶不住要进行Full GC时开启内存碎片合并整理过程。
- 此过程无法并发处理,停顿时间不得不变长
适用场景:
- 对于追求服务响应速度,希望GC停顿短的场合十分适用。如网站服务端
G1 收集器
基本原理:
- G1 = Garbage-First
- 面向服务端应用的垃圾收集器
- 虽然保留了新老年代的概念,但是两者不再是物理隔离了,它们都是一部分Region(不需要连续)的集合
优点:
- 并行与并发:充分利用多核来缩短Stop-The-World时间。G1收集器仍然可以通过并发的方式让Java程序继续执行
- 分代回收
- 空间整合:不会产生空间碎片,收集后能提供规整的可用内存,利于程序长时间执行
- 可预测的停顿:让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在GC上的时间不得超过N毫秒
- 原因:G1可以有计划地避免在整个Java堆中进行全区域的垃圾回收
运作过程(与CMS有一定的相似之处):
- 初始标记(Initial Marking)
- 并发标记(Concurrent Marking)
- 最终标记(Final Marking)
- 筛选回收(Live Data Counting and Evacuation)