JVM垃圾收集器汇总(不包括ZGC、Shenandoah GC)

55 阅读6分钟

JVM 垃圾收集器

image.png
1、Serial是一个单线程收集器,在它进行垃圾收集时,必须暂停其他所有工作线程(Stop The World);简单高效,是虚拟机在Client模式下默认的新生代收集器(复制算法)。停顿时间在几十到一百多毫秒以内,可以接受。

2、ParNew其实就是Serial收集器的多线程版本;ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器。除去性能因素,很重要的原因是除了Serial收集器外,目前只有它能与CMS收集器(老年代)配合工作。(复制算法)

image.png 但是,在单CPU环境中,ParNew收集器绝对不会有比Serial收集器更好的效果,甚至由于存在线程交互的开销,该收集器在通过超线程技术实现的两个CPU的环境中都不能百分之百地保证可以超越Serial收集器。然而,随着可以使用的CPU的数量的增加,它对于GC时系统资源的有效利用还是很有好处的。

3、Parallel Scavenge收集器是新生代垃圾收集器,使用复制算法,也是并行的多线程收集器。与ParNew收集器相比,很多相似之处,但是Parallel Scavenge收集器更关注可控制的吞吐量(运行用户代码时间/(运行用户代码+垃圾收集时间))。吞吐量越大,垃圾收集的时间越短,则用户代码则可以充分利用CPU资源,尽快完成程序的运算任务。

直观上,只要最大的垃圾收集停顿时间越小,吞吐量是越高的,但是GC停顿时间的缩短是以牺牲吞吐量和新生代空间作为代价的。比如原来10秒收集一次,每次停顿100毫秒,现在变成5秒收集一次,每次停顿70毫秒。停顿时间下降的同时,吞吐量也下降了。

4、Serial Old收集器是Serial收集器的老年代版本,也是一个单线程收集器,采用“标记-整理算法”进行回收。其运行过程与Serial收集器一样。

Serial Old收集器的主要意义也是在于给Client模式下的虚拟机使用。如果在Server模式下,那么它主要还有两大用途:一种用途是在JDK 1.5以及之前的版本中与Parallel Scavenge收集器搭配使用,另一种用途就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。

5、Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法进行垃圾回收。其通常与Parallel Scavenge收集器配合使用,“吞吐量优先”收集器是这个组合的特点,在注重吞吐量和CPU资源敏感的场合,都可以使用这个组合。

6、 CMS (Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,基于“标记-清除”算法,从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的(有的过程也是StopTheWorld)

CMS分为四个步骤:初始标记(GCRoots能直接关联到的对象,速度快,可达性分析,Stop The World),并发标记(可达性分析),重新标记(修正并发标记期间因用户程序继续运作而导致的变动,速度快,Stop The World),并发清除

CMS的优点很明显:并发收集、低停顿。由于进行垃圾收集的时间主要耗在并发标记与并发清除这两个过程,虽然初始标记和重新标记仍然需要暂停用户线程,但是从总体上看,这部分占用的时间相比其他两个步骤很小,所以可以认为是低停顿的。

缺点:

对CPU资源太敏感,这点可以这么理解,虽然在并发标记阶段用户线程没有暂停,但是由于收集器占用了一部分CPU资源,导致程序的响应速度变慢

CMS收集器无法处理浮动垃圾。所谓的“浮动垃圾”,就是在并发标记阶段,由于用户程序在运行,那么自然就会有新的垃圾产生,这部分垃圾被标记过后,CMS无法在当次集中处理它们(为什么?原因在于CMS是以获取最短停顿时间为目标的,自然不可能在一次垃圾处理过程中花费太多时间),只好在下一次GC的时候处理。这部分未处理的垃圾就称为“浮动垃圾”。由于垃圾收集阶段用户线程还需要运行,那就不能等老年代几乎全满了再收集,一般达到92%时就开始收集,而CMS运行期间预留的内存无法满足程序需要,就会出现“Concurrent Mode Failure”,此时将启动备用方案serial old

由于CMS收集器是基于“标记-清除”算法的(可能是为了时间短),前面说过这个算法会导致大量的空间碎片的产生,一旦空间碎片过多,大对象就没办法给其分配内存,那么即使内存还有剩余空间容纳这个大对象,但是却没有连续的足够大的空间放下这个对象,所以虚拟机就会触发一次Full GC。

image.png 在使用CMS收集老年代时,新生代只能选用ParNew或者Serial收集器中的一个(CMS与其他不配套,其他的没有使用传统的GC收集器框架)

7、G1(Garbage-First)收集器,JDK1.7才开始商用。使用G1收集器时,Java堆内存布局与其他收集器有很大差别,它将整个Java堆分为多个大小相等的独立区域(Region),虽然还保留新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,他们都是Region(不需要连续)的集合。

特点:并行与并发。分代收集(不需要其他收集器配合)。空间整合(整体来看采用“标记-整理”,局部(两个Region之间)采用复制)。可预测的停顿。

G1跟踪各个Region里面的垃圾堆积价值大小(回收所获得的空间大小以及回收所需的时间),在后台维护一个优先列表,每次优先收集价值最大的Region(所以叫Garbage-First),从而保证了G1在有限时间内可以获取尽可能高的收集效率。

(老年代)过程:初始标记(Stop The World)、并发标记、最终标记(Stop The World)、筛选回收(Stop The World)

G1的YoungGC就是将E区和S区复制到灰色的空白区。

G1中有Humongous区(巨大区)用于存放比标准块大50%的对象

image.png

image.png