JVM - 垃圾回收器概述

867 阅读4分钟

既然选择了远方,即使天寒地冻,路遥马亡,我本就一无所有,又有何惧。

串行收集器

  1. 单线程、独占式进行垃圾回收
  2. 独占式:应用程序线程会停止工作,只有垃圾回收线程在工作,即 stop the world
  3. 在并行能力较差的机器上,会有更好的性能表现。

新生代串行收集器

  1. 新生代串行收集器使用的是 复制算法
  2. 使用 -XX:UseSerialGC, -XX:+UseConcMarkSweepGC -XX:-UseParNew 启用
  3. GC 日志如下所示
[GC (Allocation Failure) [DefNew: 67932K->0K(78720K), 0.0002327 secs] 68792K->859K(253504K), 0.0002491 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

老年代串行收集器

  1. 老年代串行收集器使用的是 标记-压缩算法
  2. 使用 -XX:UseSerialGC-XX:+UseParNewGC 启用
  3. GC 日志如下
[Full GC (Allocation Failure) [Tenured: 10250K->10249K(15360K), 0.0187416 secs] 10250K->10249K(19968K), [Metaspace: 9228K->9228K(1058816K)], 0.0187608 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

并行收集器

ParNew

  1. 新生代垃圾回收器
  2. 新生代串行收集器的多线程版本
  3. 与新生代串行收集器的区别仅在于垃圾回收时,是多线程并行。
  4. 使用 -XX:UseParNew-XX:UseConcMarkSweepGC 启用
  5. GC 日志如下
[GC (Allocation Failure) [ParNew: 82K->24K(4608K), 0.0003178 secs][Tenured: 15353K->10251K(15360K), 0.0185459 secs] 15435K->10251K(19968K), [Metaspace: 8855K->8855K(1056768K)], 0.0188925 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 

ParallelGC

  1. 新生代垃圾回收器,使用 复制算法
  2. ParNew 相比,ParallelGC 更关注吞吐量
  3. -XX:MaxGCPauseMillis:设置最大垃圾停顿时间。例:-XX:MaxGCPauseMillis=200
  4. -XX:GCTimeRatio:设置吞吐量大小。取值范围为 0~100 的整数。若值为 n,那么 JVM 将花费不超过 1/(1+n) 的时间在 GC 上。例:-XX:GCTimeRatio=99
  5. -XX:+UseAdaptiveSizePolicy:自适应调整 新生代大小、eden 和 survivor 比例,以及晋升老年代对象年龄等参数
  6. 可通过 -XX:+UseParallelGC-XX:+UseParallelOldGC 启用
  7. GC 日志如下
[GC (Allocation Failure) [PSYoungGen: 2410K->512K(4608K)] 12650K->10947K(19968K), 0.0016662 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 

ParallelOldGC

  1. 老年代垃圾回收器,使用 标记-压缩算法
  2. ParallelGC 一致,关注系统吞吐量ParallelOldGCjdk1.6后(包括) 才可以使用
  3. -XX:ParalleGCThreads:设置 GC 时,并行的线程数
  4. 可通过 -XX:+UseParallelGC-XX:+UseParallelOldGC 启用
  5. GC 日志如下
[Full GC (Allocation Failure) [PSYoungGen: 480K->0K(4608K)] [ParOldGen: 10499K->626K(15360K)] 10979K->626K(19968K), [Metaspace: 3063K->3063K(1056768K)], 0.0043604 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 

CMS

CMS 是一款 低停顿老年代 垃圾收集器。一般与 Serial, ParNew 新生代收集器一起工作,默认是 ParNew。 工作流程细化为以下几个步骤

  1. 初始化标记(stop the world
  2. 并发标记
  3. 预清理
  4. 重新标记(stop the world
  5. 并发清理
  6. 并发重置状态等待下次 CMS 触发

初始化标记

  1. stop the world
  2. 标记老年代 GC Root 对象
  3. 标记新手代引用老年代的对象

并发标记

  1. 与应用线程并发执行,从上一步标记的节点顺着引用链路往下标记
  2. 并发标记过程中,老年代会产生新的对象、老年代引用会变更等等。为了提高重新标记的效率,这些对象所在的 card 会被标记为 dirty
  3. 这一阶段,可能会导致 concurrent mode failure

预清理

与应用线程并发执行,处理上一个阶段被标记为 dirty 的对象。该阶段为了减少 重新标记 产生的停顿时间,有可能会等待一次 ygc

重新标记

  1. stop the world
  2. dirtyroot 继续往下标记可达对象

并发清理

  1. 与应用线程并发执行
  2. 采用 标记-清除 算法将垃圾清除

concurrent mode failure

在并发清理阶段,提到,有可能会发生 concurrent mode failure 现象。 出现该现象的本质原因如下: 老年代没有足够的空间分配对象,从而导致使用 Serial Old 垃圾收集器触发一次 Full GC

主要参数

  1. -XX:ConcGCThreads, -XX:ParallelCMSThreads
    CMS 默认启动的并发线程是(ParallelGCThreads + 3)/ 4. ParallelGCThreads 表示新生代 GC 线程数量。-XX:ConcGCThreads -XX:ParallelCMSThreads 可手动指定 CMS 并发线程

  2. -XX:+CMSScavengeBeforeRemark 在进行 重新标记 阶段时,会执行一次 ygc

  3. -XX:CMSInitiatingOccupancyFraction 默认68,当老年代空间使用率达到该值时,会执行一次 CMS GC

  4. -XX:+UseCMSCompactAtFullCollection 使 CMS 在垃圾收集完成后,进行一次内存碎片整理,碎片整理会 stop the world

  5. -XX:CMSFullGCsBeforeCompaction 默认0,设定进行多少次 CMS 回收后,进行一次内存压缩

  6. -XX:CMSMaxAbortablePrecleanTime 默认 5000 毫秒,预清理阶段,等待 ygc 最大时间

  7. -XX:+CMSClassUnloadingEnabled 允许回收 Class

垃圾回收器组合

笔者无法在 jdk1.8 上,测试出 serial + parallel old 组合。

参考

CMS垃圾收集器