《JVM第9课》垃圾回收器

先来看一张图,串行代表两个垃圾回收器按顺序执行,并行代表同时执行。STW代表工作线程暂停,Stop The World的意思。

垃圾回收器 执行顺序 执行方式 作用区域 使用算法 说明
Serial GC 串行 工作线程暂停,单线程进行垃圾回收 新生代 复制算法
Serial Old GC 串行 工作线程暂停,单线程进行垃圾回收 老年代 标记-整理算法
ParNew GC 并行 工作线程暂停,多线程进行垃圾回收 新生代 复制算法 Serial GC的多线程版
CMS GC 并行 用户线程和垃圾回收线程同时执行 老年代 标记-清除算法 低暂停
Parallel GC 并行 工作线程暂停,多线程进行垃圾回收 新生代 复制算法 和ParNew相比能动态调整内存分配情况JDK8默认
Parallel Old GC 并行 工作线程暂停,多线程进行垃圾回收 老年代 标记整理算法 替代串行的Serial Old GC
G1 并行 用户线程和垃圾回收线程同时执行 整堆 分区算法 在延迟可控的情况下尽可能提高吞吐量JDK9默认
ZGC 并行 用户线程和垃圾回收线程同时执行 整堆 分页算法 STW的时间不超过1ms,且不会随着堆的大小增加而增加

常用命令:

-XX:+PrintCommandLineFlags,查看使用的垃圾收集器

-XX:+UseSerialGC,指定使用Serial GC处理新生代,并且默认会使用Serial Old GC处理老年代

-XX:+UseParNewGC,指定新生代使用ParNew GC,-XX:+UseConcMarkSweepGC,指定老年代使用CMS GC

-XX:+UseParallelGC,指定新生代使用Parallel GC,-XX:+UserParallelOldGC,指定老年代使用Parallel Old GC,这两个配置一个,另一个自动激活

1. Serial GC、Serial Old GC

Serial GC和Serial Old GC是比较早的垃圾回收器,那个时候一般的服务器CPU还只有一核,所以一个线程进行垃圾回收就够了。串行的意思就是两个垃圾回收器不能同时执行。

2. Parallel GC、Parallel Old GC

Parallel GC进行垃圾回收时也会暂停用户线程(STW),与Serial GC的不同就是它时多线程进行垃圾回收的。下面画个图方便理解垃圾回收过程。

在一次垃圾回收过程中,会进行一次STW,并且会有多个线程同时进行垃圾回收,回收完后用户线程回复运行。

3. CMS GC

CMS全称是ConcMarkSweep,即并发标记清除。但是它不是某个JDK默认的,要使用的话就加上-XX:+UseConcMarkSweepGC参数。

CMS GC是Java9之前我们很常用的垃圾回收器,是针对老年代的使用标记-清除算法的垃圾回收器。我们一般会使用CMS GC替代默认的老年代垃圾回收器,因为CMS GC的特点是低暂停。实现低暂停的方法是在初始标记阶段只标记GC Roots直接可达的对象,这个阶段比较短,所以对用户线程来说是低暂停。

下面用一张图展示它的垃圾回收过程:

阶段一,初始标记:

  • STW,暂停所有用户线程。
  • 然后标记GC Roots直接可达的对象,就是第一层对象。
  • 一旦标记完就恢复用户线程继续执行。
  • 这个阶段很快。

阶段二,并发标记:

  • 从上一个阶段标记出的对象,开始遍历整个老年代,标记出所有的可达对象。
  • 耗时虽然比较长,但好在不需要STW,可与用户线程一起执行。
  • 采用的是三色标记算法(大家自行拓展)。

阶段三,重新标记:

  • 由于在并发标记阶段,应用程序线程也在运行,可能会导致一些对象的状态发生变化,例如新的对象被创建或旧的对象被删除。
  • 为了修正这些问题,需要进行一次短暂的停顿,重新检查并更新标记状态
  • 这个阶段也是"Stop-The-World"的,但也不会太长。

阶段四,并发清理:

  • 清除垃圾对象,释放空间供后续分配使用。
  • 清理工作与应用程序线程并发进行,不会引起停顿。

阶段五,并发重置:

  • CMS收集器会准备下一次垃圾回收循环,重置内部数据结构等。
  • 这一阶段也是与应用程序并发进行的。

CMS整个垃圾回收过程更长了,但是STW的时间变短了,而且在垃圾回收过程中大部分时间用户线程可以执行,所以用户体验更好了。

在并发清理过程中,可能用户线程会产生新的垃圾,这些就是"浮动垃圾",只能等到下一次GC时来清理。

如果在并发标记、并发清理过程中,用户线程产生的新对象要进入老年代,但是老年代空间又不够,那么就会导致"concurrent mode failure",此时就会利用Serial Old来做一次垃圾回收,就会做一次全局STW。

由于采用的是标记-清除算法,所以会产生内存碎片,可以通过参数 -XX:+UseCMSCompactAtFullCollection 让JVM在执行完标记-清除后再做一次整理,也可以通过 -XX:CMSFullGCsBeforeCompaction 来指定多少次GC后来做整理,默认是0,表示每次GC后都整理。

CMS垃圾回收器能够在大多数时间内与应用程序并发执行,减少了因垃圾回收引起的停顿时间。然而,CMS也有一些缺点,比如它可能产生大量的浮动垃圾(floating garbage),并且在某些情况下仍然会导致较长的停顿。随着Java版本的发展,G1 GC和ZGC等更先进的垃圾回收器逐渐取代了CMS的地位。

4. G1(Garbage-First)

G1垃圾回收器是在 Java 7 update4 之后引入,并在Java 9中成为默认的垃圾收集器的。G1的设计目标是提供高吞吐量的同时,实现可预测的暂停时间,特别适合处理大堆内存的应用场景。下面介绍一些G1垃圾回收器的内存结构。

先看张图:

之前我们讲的垃圾回收器把堆内存分为Eden、S0、S1、老年代这四个区域,每个区域都是连续的。而在G1中,整块堆内存被分成2048个大小相等的region,这些region在物理上不要求是连续的,但在逻辑上是连续的。每个region可以独立地作为Eden、Survivor或老年代的一部分。所以说G1还是把内存分为了Eden、Survivor、老年代区,只不过空间可以是不连续的了。

Humongous区是专门用来存放大对象的(如果一个对象大小超过了一个region的50%,那么就是大对象)

用一张图展示垃圾回收过程:

阶段一,初始标记:

  • 短暂的STW。
  • 标记出直接可达对象。

阶段二,并发标记:

  • 并发遍历整个堆,标记所有可达对象。
  • 不需要STW。
  • 三色标记。

阶段三,最终标记:

  • 这也是一个较短的STW。
  • 修正并发标记期间由于应用程序运行而可能产生的误差。

阶段四,筛选回收:

  • 需要STW,来清除垃圾对象。
  • 可通过 -XX:MaxGCPauseMillis 来指定GC的STW停顿的时间,所以可能并不会回收掉所有的垃圾对象,默认为200ms(略小于人的平均反应速度)。
  • 由于回收时间有限,所有会选择更有回收价值(垃圾更多)的区域进行回收。这也是Garbage-First(垃圾优先)的名字由来。
  • 采用的是复制算法,不会产生碎片(会把某个region里的可达对象移动到空白的region里)。

由于筛选回收阶段的STW时间可能不够用导致垃圾约积越多,为了避免OOM,G1 中还提供了另外三种垃圾回收模式:Young GC、Mixed GC 和 Full GC,它们有各自的触发条件。

Young GC: Eden区满,就会触发G1的YoungGC,对Eden区进行垃圾回收。

Mixed GC: 老年代的占用率达到了 -XX:InitiatingHeapOccupancyPercent 指定的百分比,回收所有的新生代以及部分老年代,以及大对象区。

Full GC: 在进行Mixed GC的过程中,采用的是复制算法,如果没有空的region可用,就会触发Full GC,会STW,并采用单线程来进行标记-整理算法进行GC,相当于一次Serial GC。

相关推荐
东阳马生架构7 分钟前
JVM简介—3.JVM的执行子系统
jvm
程序员志哥7 小时前
JVM系列(十三) -常用调优工具介绍
jvm
后台技术汇7 小时前
JavaAgent技术应用和原理:JVM持久化监控
jvm
程序员志哥7 小时前
JVM系列(十二) -常用调优命令汇总
jvm
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭7 小时前
聊聊volatile的实现原理?
java·jvm·redis
_LiuYan_10 小时前
JVM执行引擎JIT深度剖析
java·jvm
王佑辉10 小时前
【jvm】内存泄漏的8种情况
jvm
工业甲酰苯胺10 小时前
JVM简介—1.Java内存区域
java·jvm·python
yuanbenshidiaos1 天前
c++---------数据类型
java·jvm·c++
java1234_小锋1 天前
JVM对象分配内存如何保证线程安全?
jvm