GC 分代回收 - 垃圾收集器

585 阅读3分钟

0. 预备知识

0.1 Stop-the-World

  • JVM 由于要执行 GC 而停止了程序的执行
  • 这种情况会在任何一种 GC 算法中发生
  • 多数 GC 优化通过减少 Stop-the-World 发生的时间来提高程序性能

0.2 Safepoint

在可达性分析中,将所有线程都停止,形成一个快照点 (Safepoint),保证了分析结果的确定性

  • 产生 Safepoint 的地方:方法调用、循环跳转和异常跳转等
  • Safepoint 数量要适中

0.3 JVM 运行模式

  • Server
  • Client
    Server 启动较慢,但是启动之后的运行速度比 Client 更快
# 查看 JVM 使用哪种运行模式
$ java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)

1. 常见垃圾收集器

1.1 年轻代中常见垃圾收集器

1.1.1 Serial 收集器

  • 单线程收集,进行垃圾收集时,会暂停所有工作线程
# 指定 GC 使用 Serial (复制算法)
-XX: +UseSerialGC

1.1.2 ParNew 收集器

  • 多线程收集,其余与 Serial 一致
  • 单核执行效率不如 Serial,多核下有优势
# 指定 GC 使用 ParNew (复制算法)
-XX: +UseParNewGC

1.1.3 Parallel Scavenge 收集器

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

  • 与 ParNew 类似,都使用多线程
  • Parallel Scavenge 相对于 ParNew 不同的是,后者更注意用户线程的等待时间,前者则是关注整个系统的吞吐量
  • JVM Server 模式下默认
# 指定 GC 使用 Parallel Scavenge (复制算法)
-XX: +UseParallelGC

1.2 常见的老年代收集器

1.2.1 Serial Old 收集器

  • 单线程收集,进行垃圾收集时,会暂停所有工作线程
  • 简单高效,Client 模式下默认的老年代收集器
# 指定 GC 使用 Serial Old (标记-整理算法)
-XX: +UseSerialOldGC

1.2.2 Parallel Old 收集器

  • 与 Parallel Scavenge 类似,只不过采用的是标记-整理算法
# 指定 GC 使用 Parallel Old (标记-整理算法)
-XX: +UseParallelOldGC

1.2.3 CMS 收集器

  • 清理线程和用户线程几乎能做到同时工作
标记和回收过程 (六步)
  • 初始标记:Stop-the-World
  • 并发标记:并发追溯标记,程序不会停顿
  • 并发预清理:查找执行并发标记阶段从年轻代晋升到老年代的对象
  • 重新标记:暂停虚拟机 (Stop-the-World),扫描 CMS 堆中的剩余对象
  • 并发清理:清理垃圾对象,程序不会停顿
  • 并发重置:重置 CMS 收集器的数据结构
# 指定 GC 使用 CMS (标记-清除算法)
-XX: +UseConcMarkSweepGC

1.3 年轻代和老年代公用的算法

1.3.1 G1 收集器

  • 并发和并行
  • 分代收集
  • 空间整合(标记-整理)
  • 可预测的停顿(指定在 M 毫秒内,消耗在 GC 上的时间不得超过 N 毫秒)
工作原理简述
  • 将 Java 堆内存划分成多个大小相等的 Region
  • 上面的特性导致了,新生代和老年代不再是物理隔离的内存空间,他们现在都是一系列 Region 的集合,在逻辑上连续
# 指定 GC 使用 G1 (复制 + 标记-清除算法)
-XX: +UseG1GC