走过路过不要错过
点击蓝字关注我们
从不同角度分析垃圾收集器,可以将其划分为不同的模型。
按线程数分,可以分为串行垃圾回收器和并行垃圾回收器;按照工作模式分,可以分为并发式垃圾回收器和独占式垃圾回收器;按碎片处理方式可分为压缩式垃圾回收器和非压缩式垃圾回收器;而按工作的内存区间,又可分为新生代垃圾回收器和老年代垃圾回收器。本文就基于工作的内存区间划分,来介绍七种经典的垃圾回收器,下图是它们的工作区间以及搭配方式。
看名字就能猜到,这个收集器是一个单线程工作的收集器,但是它的“单线程”的意义并不仅仅是说明它只会使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它工作结束。同时,其采用的是标记复制算法。
Serial 是 JDK 1.3.1 之前新生代的唯一选择,虽然看起来这个垃圾收集器好像老而无用了。但是事实上它依然是 HotSpot 虚拟机运行在客户端模式下的默认新生代垃圾收集器,有着优于其他收集器的地方,那就是简单而高效(与其他收集器的单线程相比),对于内存资源受限的环境,它是所有收集器里额外内存消耗(Memory Footprint)最小的;
ParNew 收集器实际上是 Serial 收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为包括 Serial 收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与 Serial 收集器完全一一致。
ParNew 收集器除了支持多线程并行收集外,其他与 Serial 收集器相比并没有太多的创新之处,但它却是不少运行在服务端模式下的 HotSpot 虚拟机,尤其是 JDK 7 之前的遗留系统首选的新生代收集器,其中有一个与功能、性能无关但其实很重要的原因:除了 Serial 收集器外,目前只有它能与 CMS 收集器配合工作。
ParNew 收集器在单核心处理器的环境中绝对不会有比 Serial 收集器更好的效果。它默认开启的收集线程与处理核心数量相同,在处理器核心非常多的环境下,可以使用 -XX:ParallelGCThreads
来限制垃圾回收器的线程数。
Parallel Scavenge 收集器也是一款新生代收集器,同样是基于标记-复制算法实现的收集器,其从表面上看与 ParNew 十分相似,但它的关注点与其他收集器不同,CMS 等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而 Parallel Scavenge
收集器的目标则是达到一个可控制的吞吐量(Throughput)。
所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗的时间的比值,即:
Serial Old 是 Serial 收集器的老年代版本,同样也是一个单线程工作的收集器,使用标记整理算法。
这个收集器的主要意义也是提供客户端模式下的 HotSpot 虚拟机使用。如果在服务端模式下,它也可能有两种用途:一种是在 JDK 5 以前的版本中与 Parallel Scavenge 收集器搭配使用,另外一种就是做为 CMS 收集器发生失败时的后备预案,在并发收集发生 Concurrent Mode Failure 时使用。
Parallel Old 是 Parallel Scavenge 收集器的老年代版本,支持多线程并发收集,基于标记整理算法。
其主要与 Parallel Scavenge 做搭配。
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。从名字上就可以看出 CMS 收集器是基于标记-清除算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为四个步骤,包括:
其中并发标记以及重新标记这两个步骤仍然需要“Stop The World”。
初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快;
并发标记就是从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时很长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行;
而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍微长一些,但也远比并发标记阶段的时间短;
最后是并发清除阶段,清除删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程并发进行的。
CMS 的优点很明显:并发收集、低停顿。但它也有很明显的缺点:
为解决CMS算法产生空间碎片和其它一系列的问题缺陷,HotSpot提供了另外一种垃圾回收策略,G1(Garbage First)算法,通过参数-XX:+UseG1GC
来启用,该算法在JDK 7u4版本被正式推出,官网对此描述如下:
The Garbage-First (G1) collector is a server-style garbage collector, targeted for multi-processor machines with large memories. It meets garbage collection (GC) pause time goals with a high probability, while achieving high throughput. The G1 garbage collector is fully supported in Oracle JDK 7 update 4 and later releases. The G1 collector is designed for applications that:
在 G1 算法中,采用了另外一种完全不同以往的组织堆内存,堆内存被划分为多个大小相等的内存块(Region),每个Region是逻辑连续的一段内存,结构如下:
每个Region被标记了E、S、O和H,说明每个Region在运行时都充当了一种角色,其中H是以往算法中没有的,它代表Humongous,这表示这些Region存储的是巨型对象(humongous object,H-obj),当新建对象大小超过Region大小一半时,直接在新的一个或多个连续Region中分配,并标记为H。
G1 中提供了三种模式垃圾回收模式,young GC、mixed GC 和 full GC,在不同的条件下被触发。
Young GC
发生在年轻代的GC算法,一般对象(除了巨型对象)都是在eden region中分配内存,当所有eden region被耗尽无法申请内存时,就会触发一次 Young GC,这种触发机制和之前的 young GC 差不多,执行完一次 Young GC,活跃对象会被拷贝到survivor region或者晋升到old region中,空闲的region会被放入空闲列表中,等待下次被使用。
Mixed GC
当越来越多的对象晋升到老年代old region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即 mixed gc,该算法并不是一个 Old GC,除了回收整个 Young region,还会回收一部分的 Old Region,这里需要注意:是一部分老年代,而不是全部老年代,可以选择哪些 Old region 进行收集,从而可以对垃圾回收的耗时时间进行控制。
mixed GC 的执行过程有点类似 CMS,主要分为以下几个步骤:
Full GC
如果对象内存分配速度过快,Mixed GC 来不及回收,导致老年代被填满,就会触发一次 Full GC,G1 的 Full GC 算法就是单线程执行的 serial old gc,会导致异常长时间的暂停时间,需要进行不断的调优,尽可能的避免 Full GC.