JVM垃圾收集器

垃圾收集器是jvm垃圾回收的具体实现,常用的垃圾收集器包括

  • 新生代:Serial 、Parallel Scavenge 、ParNew
  • 老年代:Serial Old、Parallel Old、CMS
  • 新生代和老年代收集器:G1

如图所示,存在连线的收集器可以搭配使用,下面分别介绍这几种收集器。

Serial (串行) 收集器

Serial / Serial Old 收集器

Serial 收集器是单线程的收集器,单线程的意思是使用一个处理器或者一个线程处理垃圾回收,在进行垃圾回收的处理期间要暂停其他的工作线程。

它的特点是简单高效,由于没有线程交互的开销,自然可以获得很高的单线程收集效率。

Serial 收集器对于运行在 Client 模式下的虚拟机来说是个不错的选择。但javaweb应用不会使用串行垃圾收集器。

Parallel Scavenge 收集器

Parallel Scavenge/ Parallel Old 收集器

Parallel Scavenge 收集器它是多线程的收集器,和ParNew收集器类似。Parallel Scavenge 收集器 也叫吞吐量优先收集器,公式为:

吞吐量 = 运行用户代码时间/(运行用户代码时间+运行垃圾收集时间)

     作为一个吞吐量优先的收集器,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。这种调节方式称为GC自适应的调节策略(GC Ergonomics)。

    ParNew 收集器

    ParNew和Serial Old收集器的组合

    ParNew 收集器使用标记复制算法,它是Serial (串行) 收集器的多线程版本。

    Server模式下,ParNew收集器是一个非常重要的收集器,因为除Serial外,目前只有它能与CMS收集器配合工作。

    CMS 收集器

    CMS垃圾收集器

    在前面的收集器中,标记操作都会暂停所有用户的线程,这样会影响用户体验,于是解决这一问题的并发标记收集器cms出现了。

    CMS收集器的全称是 Concurrent Mark Sweep 并发标记清除 。分为4个阶段

    1. 初始标记: 暂停所有的其他线程,并记录下直接与 GC Roots 相连的对象,速度很快 。
    2. 并发标记: 从GC Roots 开始对堆中对象进行可达性分析,递归扫描整个堆里面的对象图,找出要回收的对象,这个阶段耗时较长,但可以和用户程序并发执行。
    3. 重新标记: 修正并发标记期间因用户程序继续运行导致标记产生变动的那一部分对象,请参考 JVM三色标记算法
    4. 并发清除: 开启用户线程,同时 GC 线程开始对未标记的区域做清扫。

    CMS收集器的优缺点

    • 优点:并发收集、低停顿
    • 缺点:对 CPU 资源敏感、无法收集浮动垃圾、 标记清除 算法带来的空间碎片。

    CMS标记算法使用的三色标记算法,请参考 JVM三色标记算法可以更好的理解CMS收集器整个收集的步骤。

    G1收集器

    G1收集器

    G1 (Garbage-First) 是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征,它是jdk1.7正式使用的全新垃圾收集器,在jdk1.9中将G1变成默认的垃圾收集器,以替代CMS。

    G1收集器的内存分布

    G1收集器的内存分布

    在G1收集器中,内存结构不再是之前的新生代和老年代的连续区域,而是将内存划分为若干个Region区域,Region是G1回收的最小单元。一般Region大小等于堆大小除以2048,比如堆大小为4096M,则Region大小为2M,当然也可以用参数-XX:G1HeapRegionSize手动指定Region大小,但是推荐默认的计算方式。它是逻辑上的划分,这样不用担心每个区域的内存是否够用,因为他们是共用 的,即一个Region区域刚开始是年轻代,回收后可以是老年代region区域。

    大对象Humongous定义

    • 如果一个对象超过了Regin区域的的50%,则存放到Humongous区域。
    • 如果一个Humongous装不下,G1会寻找连续的Humongous区域存放。

    使用-XX:G1HeapRegionSize来指定Region的大小,Region的大小必须是2的幂次方,最大32M。

    G1 Rset 集合

    G1 Rset 集合

    Rset集合的全称是remembered set,该集合用来记录并跟踪其它Region指向该Region中对象的引用。说直白点,就是记录别人引用我的记录。

    Rset的内存结构类似于Hashtable<key,int[]>,其中key是其它的Regin,int[]数组是其它Regin所对应的卡表(Card Table)。
    在上面的例子中
    Regin1区域中的第1和第3块存在对Regin2的引用。
    Regin3区域中的第2和第3块存在对Regin2的引用。
    也就是说Regin中的Rset集合记录的是其它的Region对自己的引用。

    有了Rset集合在G1垃圾回收中就不需要扫描整个堆区域,只需要通过Rset集合知道它们的引用关系。

    G1的设计原则

    简化JVM性能调优,开发人员只需要简单的三步即可完成调优:

    1. 第一步,开启G1垃圾收集器 
    2. 第二步,设置堆的最大内存 
    3. 第三步,设置最大的停顿时间 G1中提供了三种模式垃圾回收模式,Young GC、Mixed GC 和 Full GC,在不同的条件 下被触发。

    G1垃圾收集器的步骤

    1. 初始标记:暂停所有的其他线程,并记录下直接与 GC Roots 相连的对象,速度很快 。
    2. 并发标记:从GC Roots 开始对堆中对象进行可达性分析,递归扫描整个堆里面的对象图,找出要回收的对象,这个阶段耗时较长,但可以和用户程序并发执行。
    3. 最终标记:修正并发标记期间的变动部分,请参考 JVM三色标记算法
    4. 筛选回收:先对各个Region区的回收价值和成本进行排序,找出回收价值最大的Region优先回收。

    G1标记算法使用的三色标记算法,请参考 JVM三色标记算法。可以更好的理解G1收集器整个收集的步骤。

    G1停顿时间

    停顿时间指的是暂停所有的用户线程,执行GC线程,针对的是G1回收步骤的最后一步筛选回收所耗费的时间。它可以通过参数-XX:MaxGCPauseMills设置,默认值是200ms。

    比如:老年代占据堆内存的45%,促发了Mixed GC,需要回收800个Region,如果一次性全部回收,则耗时会大于200ms,为了解决这个问题,就会找出回收价值最大的600个Region进行回收。

    这也是G1区别于其它收集器的特征。

    G1垃圾收集器分类

    Young GC:Young GC会回收所有的Eden和Survivor对象并将存活对象复制到Old区和另一部分Survivor区,触发条件是达到最大阈值并且无法申请内存的时候。

    Mixed GC:老年代占据堆内存45%可以通过参数-XX:InitiatingHeapOccupancyPercent修改这个阈值),就会促发Mixed GC,此时对年轻代和老年代都会进行回收。

    Full GC:Full GC指的是针对新生代、老年代、永久代的全体内存空间的 垃圾回收,所以称之为Full GC。