一、GC分类与性能指标

  • 不同厂商、不同版本的JVM可有自己的实现。
  • 不同角度有不同分类。

Java不同版本的新特性

  1. 语法层面:
    Lambda,switch,自动装箱、拆箱、enum、泛型。
  2. API层面:Stream API,新的日期时间,Optional,String,集合框架
  3. 底层优化:
    JVM的GC变化、元空间、静态域、字符串常量池等。

1. 分类

1) 按线程数分

  • 串行垃圾回收器
    • 适用于单核或较小内存等资源受限的场景
  • 并行垃圾回收器
    • 适用于并发能力较强的CPU。
    • 并行使得多个CPU同时执行,提升了应用的吞吐量。
    • 仍然采用独占式的STW机制

2)按工作模式分

  • 并发式
    • GC线程与用户线程交替工作
    • 尽可能减少用户线程停顿时间。
  • 独占式
    • GC时候停止用户线程(STW)

3)按碎片处理方式

  • 压缩式:执行碎片空间处理。
  • 非压缩式:不执行碎片空间处理。

4) 按工作内存区间

  • 年轻代垃圾回收器
  • 老年代垃圾回收器

2. 性能指标

吞吐量暂停时间内存占用三者共同构成【不可能三角】,尽可能满足其中两项。

目前的标准:在最大吞吐量优先的情况下,降低停顿时间

1) 吞吐量(Throughput)

用户代码运行时间占比总运行时间。

吞吐量与暂停时间.svg

2) 垃圾收集开销

吞吐量的补数,GC时间占比总运行时间。

3) 暂停时间

4) 收集频率

相对于应用程序执行,收集操作的发生频率。

5) 内存占用

Java堆区所占内存的大小。

6) 快速

一个对象从诞生到被回收经历的时间。

二、不同垃圾回收器概述

1. 发展历史

  1. 1999年随JDK1.3.1一起来的是串行方式的serial GC ,它是第一款GC。ParNew垃圾收集器是serial收集器的多线程版本
  2. 2002年2月26日,JDK1.4.2Parallel GCConcurrent Mark Sweep GC一起发布
  3. JDK6之后Para1lel Gc成为HotSpot默认GC.
  4. 2012年,在JDK1.7u4版本中,G1可用
  5. 2017年,JDK9G1变成默认的GC,以替代CMS。
  6. 2018年3月,JDK10G1 GC并行完整垃圾回收,实现并行性来改善最坏情况下的延迟。
  7. 2018年9月,JDK11发布。引入Epsilon(ε)垃圾回收器,又被称为【No-op】(无操作)回收器。同时,引入ZGC:可伸缩的低延迟垃圾回收器(Experimental)。
  8. 2019年3月,JDK12发布。增强G1 GC,自动返回未用堆内存给操作系统。同时,引入Shenandoah GC:低停顿时间的GC (Experimental)。
  9. 2019年9月,JDK13发布。增强ZGC,自动返回未用堆内存给操作系统。
  10. 2020年3月,JDK14发布。删除CMS垃圾回收器。扩展ZGC在macos和windows
    上的应用

2. 垃圾收集器之间的配合关系

垃圾收集器的关系.svg

  • 没有最优秀的垃圾回收器,只能根据使用场景选出最优的组合。

查看默认垃圾回收器

  1. VM参数
    -XX:+PrintCommandLineFlags (查看命令行相关参数,包含使用的垃圾回收器)
  2. 使用命令行指令
    jinfo -flag [相关垃圾回收器参数] [进程ID]

三、Serial回收器:串行回收

1. 年轻代——Serial

  1. JDK 1.3之前的唯一选择。
  2. Hotspot VM在Client模式下,新生代默认
  3. 采用复制算法串行回收STW机制

2. 老年代——Serial Old

  1. Client模式下,老年代默认。
  2. 采用标记-压缩算法串行回收STW机制
  3. Server模式下,两个用途:
    • 新生代的Parallel Scavenge配合使用。
    • 作为CMS的后备方案

3. 优势

简单而高效。对于限定单CPU环境来说,无线程间交互的开销。

4. 使用

VM参数:-XX:+UseSerialGC

四、ParNew回收器:并行回收

1. 概述

  1. 是Parallel New缩写,用于处理新生代的垃圾回收,是Serial的多线程版本(新生代回收频繁,并行方式更高效)。
  2. 采用复制算法STW机制
  3. 很多JVM在Server模式下新生代默认。
  4. 适用于多核CPU环境,充分利用硬件资源提升吞吐量。在单核CPU环境,性能不及Serial。

2. 适用

  1. VM参数设置启用:
    • -XX:+UseParNewGC
  2. 限制线程数量:
    • -XX:ParallelGCThreads,默认等于CPU线程数

五、Parallel 收集器:吞吐量优先

Java 8中默认组合。

1. 年轻代——Parallel Scavenge

  1. 采用复制算法并行回收STW机制
  2. 可控制的吞吐量
    • 适合后台运算不需要太多交互的场景。
  3. 自适应调节策略。

2. 老年代——Parallel Old

  1. JDK 1.6时提出,用于替换Serial Old
  2. 采用标记-压缩算法并行回收STW机制

3. 参数设置

  • -XX:+UseParallelGC:手动指定年轻代使用Parallel并行收集器。
  • -XX:+UseParallelOldGC:手动指定老年代使用Parallel并行收集器。

以上两个参数是一组,设置一个后,会互相激活

  • -XX:ParallelGCThreads:设置年轻代并行收集器线程数。最好与CPU核心数相等,过多线程影响性能。
    • CPU核心数 ≤ 8——等于cpu_count
    • CPU核心数>8—— 3 + (5 * cpu_count) / 8
  • -XX:MaxGCPauseMillis:设置STW最大停顿时间,单位ms。需谨慎使用
    • 为了尽可能满足(可能会超过)该限制,JVM工作时会适当调整堆大小其他参数
    • 用户来讲,停顿时间越短越好。服务端注重高并发及吞吐量,所以更适合Parallel进行控制。
  • -XX:GCTimeRatio:设置垃圾收集时间占比总时间。用于衡量吞吐量的大小。
    • 取值范围(0, 100),默认99。
    • 与上一个参数-XX:MaxGCPauseMillis有一定的矛盾性。
  • -XX:+UseAdaptiveSizePolicy:设置Parallel Scavenge的自适应调节策略
    • 这种模式下,会自动调整年轻代大小Eden和Survivor比例老年代晋升年龄等。来确保堆大小、吞吐量、停顿时间之间的平衡点。
    • 适用于手动调优困难的场合。仅需指定最大堆内存吞吐量停顿时间
    • 默认开启状态。

六、CMS回收器:低延迟

1. 概述

  • 老年代收集器,无法与Parallel Scavenge配合工作。
  • JDK 1.5时,适用于强交互应用的并发垃圾收集器Concurrent-Mark-Sweep
  • 是Hotspot第一款真正意义上的并发垃圾收集器,实现用户线程垃圾回收线程并发工作
  • 关注点:缩短用户线程停顿时间,达到交互效果。
  • 采用标记-清除算法STW机制

2. 运行过程

CMS工作原理.svg

1) 初始标记

  • STW机制,停止所有用户线程
  • 标记出GC Roots能直接关联的对象。
  • 速度非常快

2) 并发标记

  • 从上一步的GC Roots直接关联的对象开始,遍历整个对象图
  • 耗时较长,与用户线程并发执行

3) 重新标记

  • 修正并发标记期间,因为用户线程继续运作而导致的关系变动的一部分对象
  • 运行时间,相比初始标记阶段稍长

4) 并发清除

  • 清理标记对象,释放内存空间。
  • 使用标记-清除(Mark-Sweep)算法无需移动活对象,所以可以与用户线程并发执行

3. 分析

  • 最耗时的并发标记并发清除阶段,用户线程不暂停,整体回收是低停顿的。
  • 用户线程不中断,要求用户线程有足够的内存可用。
  • 回收时机:堆内存达到一定阈值
  • 预留内存不够,就会出现一次【Concurrent Mode Failure】失败。此时采用备选方案:临时启用Serial Old进行老年代GC,停顿时间会更长一些。
  • 为什么不用标记-压缩(Mark-Compact)算法?
    • 并发清除,保证用户线程执行,不能修改存活对象的地址

1) 优势

  • 并发收集
  • 低延迟

2) 劣势

  • 产生内存碎片。不能修改存活对象的地址。碎片化严重导致提前触发Full GC
  • 对CPU资源非常敏感。并发阶段,不会停顿用户线程,但会导致应用程序变慢总吞吐量降低
  • 无法处理浮动垃圾。【并发标记】阶段仅修正【怀疑是垃圾但不是垃圾】的对象。该阶段用户线程产生的垃圾对象不在初始阶段GC Roots中,【变成垃圾】的对象无法处理

4. 参数设置

  • -XX:+UseConcMarkSweepGC
    • 手动指定CMS,同时新生代GC自动绑定为ParNew(-XX:+UseParNewGC)。
  • -XX:CMSInitiatingOccupanyFraction
    • 堆内存达到该阈值时,开始执行GC。
    • JDK 1.5默认68,JDK 1.6默认92。
    • 该选项可有效降低Full GC次数
  • -XX:+UseCMSCompactAtFullCollection
    • 开启Full GC后,对内存空间进行压缩整理。
  • -XX:CMSFullGCsBeforeCompaction=m
    • Full GC执行m次后,对内存空间压缩整理。
  • -XX:ParallelCMSThreads=n
    • 设置CMS线程数。默认 (ParallelGCThreads + 3) / 4

5. 小结

  • 最小化内存,并行开销,选Serial GC
  • 最大化应用程序吞吐量,选Parallel GC
  • 最小化GC中断时间,选择CMS GC

七、G1回收器:区域化分代式

1. 设计初衷

  1. 业务越来越庞大、复杂,用户越来越多。
  2. 适应不断扩大的内存和不断增加的处理器数量
  3. 延迟可控的情况下尽可能提高吞吐量,希望它是一款【全功能收集器】。

2. 命名起源(Garbage First)

  1. G1是一个并行回收器,使用不同的Region来表示Eden,S0,S1,Old区域。
  2. 有计划地避免在整个Heap空间全区域垃圾回收。跟踪各个Region垃圾堆积价值大小(回收空间大小所需时间的经验值),在后台维护一个优先列表。每次根据运行收集的时间,优先回收堆积价值最大的Region。
  3. 【垃圾优先Garbage First】。侧重于回收垃圾最大量的Region。

3. 概述

  1. 面向服务端应用。针对配备多核CPU大容量内存的机器。
  2. G1在JDK 7开始启用实验标识,JDK 9成为默认。CMS在JDK 9被标识废弃,在JDK 14中移除。

4. 优劣

1) 优势

  1. 并行与并发
    • 并行性。可以有多个GC线程同时工作,有效利用多核计算能力,此时用户线程
      STW。
    • 并发性。部分工作可以与应用线程交替执行,不会在整个回收阶段完全阻塞。
  2. 分代收集

G1_GC.jpg

  • 堆空间划分为若干区域(约2048块区域),这些区域中包含了逻辑上的年轻代老年代。物理上可以不连续,通过动态分配实现逻辑上的连续性。
  • 回收后,区域可以更换类型,不用再坚持固定大小和固定数量。
  • 兼顾了年轻代和老年代。
  • 新增Humongous内存区域,存储大对象超过1.5个region
    • 避免短期存活的大对象对GC造成父面影响。
    • 若H区放不下,则寻找连续的H区。
    • 若找不到连续的H区,则执行Full GC。
      1. 空间整合。两种算法都可以避免内存碎片。
  • Region之间复制算法。
  • 整体上是标记-压缩算法。
  • 在Java堆比较大的时候,G1优势更明显。
    1. 可预测停顿时间模型(即软实时,soft real-time)。
  • 用户可指定在长度为M毫秒的时间片内,消耗在 GC上的时间不超过N毫秒。
  • 分区的设计决定了,G1可以只选取部分区域进行内存回收。
  • 根据允许的时间,优先回收价值最大的Region。
  • 相较于CMS,G1未必能做到CMS的最好情况的延时停顿,但最差情况要好很多。

2) 劣势

  1. 不具有压倒性优势。G1的GC内存占用额外负载都比CMS高。
  2. G1大内存上更占优势,CMS小内存更占优势。平衡点在6~8GB之间。

5. 参数设置

  • -XX:+UseG1GC
    手动指定G1执行垃圾收集
  • -XX:G1HeapRegionSize
    设置每个Region的大小。值是2的幂。范围是1MB到32MB之间。目标是根据Java堆大小划分出大约2048个区域。
  • -XX:MaxGCPauseMillis
    期望的GC停顿时间上限。JVM尽量达到,可能会超过该值。
  • -XX:ParallelGCThread
    设置STW时GC线程数的值,最多设置为8。
  • -XX:ConcGCThread
    并发标记线程数。一般为并行垃圾回收线程数(-XX:ParallelGCThread)1/ 4 左右。
  • -XX:InitiatingHeapOccupancyPercent
    设置触发并发GC堆占用率阈值,默认45。

6. 适用场景

  1. 面向服务端应用,具有大内存多处理器的机器。
  2. 需要GC延迟低的场景。
  3. 替换CMS收集器的情景。
    • 超过50%的Java堆被活动数据占用。
    • 对象分配频率年代提升频率变化很大。
    • GC停顿时间过长(长于0.5s至1s)。
  4. 在并发处理时,G1的GC线程若处理较慢,系统会调用应用程序线程帮助加速GC过程

7. 回收过程

Remembered Set 解决跨代引用需全区域扫描的问题

G1收集器_rset简图.svg

回收过程

1) 年轻代GC(Young GC)

G1_年轻代GC.png

1. 概述
  1. 触发条件
    • Eden区快用尽
  2. 回收类型
    • 并行独占式回收(Parallel + STW)
  3. 回收操作
    • 从年轻代Eden移动对象到Survivor或老年代。

2. 回收过程详述
  1. 第一阶段:扫描GC Roots
    • 类变量当前方法栈的局部变量表等。
  2. 第二阶段,更新RSet
    • 处理dirty card queue中的card,更新RSet。
    • dirty card queue是一个缓存引用关系的队列。如果在引用赋值的时候就更新RSet,需要线程间同步,无疑会极大降低应用线程的性能。队列的性能会好很多。
    • 该过程后,RSet可以准确反映老年代对所在内存分段中的引用。
  3. 第三阶段,处理RSet。
    • 识别被老年代对象指向的Eden中的对象。这些被指向的Eden中的对象可以认为是存活的对象。
  4. 第四阶段,复制对象。
    • 采用复制算法处理年轻代,复制到Survivor或老年代。
  5. 第五阶段,处理引用。
    • 处理Soft,Weak,Phantom,Final,JNI等引用。

最终Eden数据为空,GC停止工作,无碎片空间。

2) 老年代并发标记(Concurrent Marking)

1. 触发条件
  • 堆空间达到一定使用率(默认45%)

2. 回收过程
  1. 初始标记。与CMS该部分一致。
    • 标记从根结点直接可达的对象。
    • STW,触发一次Young GC。
  2. 根区域扫描Root Region Scanning)。
    • 扫描Survivor区直接可达的老年代对象,并标记被引用的对象。
    • 该过程必须在Young GC之前完成。
  3. 并发标记Concurrent Marking)。
    • 整个堆空间进行标记,该过程可能被Young GC中断。
    • 若发现区域中所有对象都是垃圾,则这个区域会立即被回收。
    • 会计算每个区域的存活对象比例(对象活性)。
  4. 再次标记Remark)。
    • 修正并发标记的结果。是STW的过程。
    • 采用了初始快照算法(snapshot-at-the-beginning),比CMS更快。
  5. 独占清理cleanup,STW)。
    • 计算各个区域存活对象和GC回收比例。
    • 为混合回收做铺垫。
  6. 并发清理阶段。
    • 识别并清理完全空闲的区域。

3) 混合回收(Mixed GC)

  • 年轻代和老年代的混合回收。
    • 年轻代回收全部。
    • 老年代选取一部分(按分配的GC时间片和Region的回收时间)进行。
  • 老年代并发标记结束后,
    • 老年代中百分百为垃圾的Region的内存分段被立即回收
    • 部分为垃圾的Region的内存分段被计算出来。
    • 默认情况下,这些老年代的内存分段会分为8次被回收(VM参数设置:-XX:G1MixedGcCountTarget)。
  • 混合回收的回收集,包括1 / 8 的老年代内存分段,eden区内存分段全部Survivor区内存分段全部
  • 混合回收的算法和年轻代回收算法完全一致,只是回收集多了老年代的内存分段
  • 由于老年代中内存分段默认分8次回收,G1优先回收垃圾多的内存分段。
    • 阈值设置:-XX:G1MixedGcLiveThresholdPercent。默认65%,垃圾占比达到该值才会回收
  • 混合回收不一定进行8次。
    • 阈值设置-XX:G1HeapWastePercent,默认10%。允许堆内存中有10%的空间被浪费。如果发现可回收的内存低于堆内存10%,则不再进行混合回收

4) Full GC(可能需要)

G1的初衷即是避免Full GC的发生,因为Full GC伴随着STW,且单线程,性能会很差,停顿时间很长。

Full GC出现的可能原因:

  • Evacuation(回收)的时候没有足够的to-space来存放晋升的对象。
  • 并发处理之前空间耗尽。

8. 补充

1) 官方设计构想

Oracle官方曾计划,回收阶段设计为与用户程序一起并发执行,但很复杂。

  • 考虑G1之回收部分Region,停顿时间用户可控,所以并不迫切实现,而将这个特性放到低延迟垃圾收集器ZGC中。
  • 同时G1不仅面向低延迟,停顿用户线程能最大幅度提高垃圾回收效率,保证吞吐量

2) 优化建议

  1. 年轻代大小:
    • 避免-Xmn或者-XX:NewRatio等相关选项显式设置年轻代大小。
    • 固定年轻代大小会覆盖暂停时间的目标
  2. 暂停时间目标,不宜太苛刻。
    • G1吞吐量目标是应用程序占90%。GC占10%;
    • 暂停时间太苛刻,表示你愿意承受更多的垃圾回收开销,直接影响吞吐量

八、垃圾回收器小结

1. 表格概况

垃圾收集器 分类 作用位置 算法 特点 适用场景
Serial 串行 新生代 复制 响应速度优先 单CPU环境的Client模式
ParNew 并行 新生代 复制 响应速度优先 多CPU环境Server模式,与CMS配合
Parallel 并行 新生代 复制 吞吐量优先 后台运算,不需太多交互场景
Serial Old 串行 老年代 标记-压缩 响应速度优先 单CPU环境的client模式
Parallel Old 并行 老年代 标记-压缩 吞吐量优先 后台运算,不需太多交互的场景
CMS 并发 老年代 标记-清除 响应速度优先 适用于互联网或B / S 业务
G1 并发 + 并行 新生代 + 老年代 1. 标记-压缩
2. 复制
响应速度优先 面向服务端应用

2. GC发展阶段

Serial => Parallel(并行) => CMS(并发) => G1 => ZGC

3. 垃圾回收器组合

垃圾收集器的关系.svg

3. 如何选择垃圾收集器

  • 优先调整堆大小,由JVM自适应完成。
  • 如果内存小于100M,串行收集器
  • 单核、单机程序,并且没有停顿时间要求,串行收集器
  • 如果是多CPU,需要高吞吐量,允许停顿时间超过1s,并行或让JVM自行选择
  • 如果是多CPU、追求低停顿时间,需快速响应(比如延迟不超过1s,互联网),使用并发收集器(官方推荐G1,互联网项目首选)。

4. 面试

垃圾收集部分,面试官可以循序渐进从理论、实践、各种角度深入,未必要求面试者都懂。如果你懂得原理,一定会成为面试中的加分项

比较通用、基础的部分如下:

  • 垃圾收集算法有哪些?如何判断一个对象是否可以被回收
  • 垃圾收集器工作的基本流程

另外,需多关注垃圾收集器中常用的参数

九、GC日志分析

1. 内存分配与垃圾回收参数列表

参数名 参数说明 图片例子
-XX:+PrintGC 输出GC日志。类似于-verbose:gc 17_垃圾收集器 - 图8
-XX:+PrintGCDetails 输出GC详细日志 17_垃圾收集器 - 图9
-XX:+PrintGCTimeStamps-XX:+PrintGCDateStamps 1. 输出GC的时间戳
2. 输出GC的时间,以yyyy-MM-ddThh:mm:ss形式
17_垃圾收集器 - 图10
-XX:+PrintHeapAtGC 进行GC前后,打印堆信息
-Xloggc:../logs/gc.log 定义日志文件的输出路径

2. 补充说明

  • [GC[Full GC说明了这次垃圾收集的停顿类型,如果有Full则说明GC发生了STW
  • 使用Serial收集器在新生代的名字是Default New Generation,因此显示的是[DefNew
  • 使用ParNew收集器在新生代的名字会变成[ParNew,意思是Parallel New Generation
  • 使用Parallel Scavenge收集器在新生代的名字是[PSYoungGen
  • 使用G1收集器的话,会显示为garbage-first heap
  • Allocation Failure
    • 表示引起GC原因是,年轻代空间不足以分配。
  • [PsYoungGen: 5986K->696K(8704K)]5986K->704K(9216K)
    • 中括号内:GC回收前年轻代大小,回收后大小,(年轻代总大小)
    • 括号外:GC回收前年轻代和老年代大小,回收后大小,(年轻代和老年代总大小)
  • user代表用户态回收耗时,sys内核态回收耗时,real实际耗时。由于多核的原因,时间总和可能会超过real时间。

3. 分代说明

1) Minor GC

GC日志分析_Minor_GC.png

2) Full GC

GC日志分析_Full_GC.png

4. 代码详细分析

1) 示例代码

  1. /**
  2. * GC日志文件位置测试:<p>&emsp;
  3. * 在 JDK 7 和 JDK 8 中分别执行。<p>
  4. * 参数列表:<p>&emsp;
  5. * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
  6. *
  7. * @author Jinhua
  8. * @version 1.0
  9. * @date 2021/5/12 22:55
  10. */
  11. public class GcLogAllocation {
  12. private static final int MB = 1024 * 1024;
  13. public static void main(String[] args) {
  14. testAllocation();
  15. }
  16. @SuppressWarnings("all")
  17. public static void testAllocation() {
  18. byte[] bytes1, bytes2, bytes3, bytes4;
  19. bytes1 = new byte[2 * MB];
  20. bytes2 = new byte[2 * MB];
  21. bytes3 = new byte[2 * MB];
  22. bytes4 = new byte[4 * MB];
  23. }
  24. }

2) 分析

GC日志分析_代码分析.png

3) 不同结果

  • JDK 7
    GC日志分析_代码分析_JDK_7.png
  • JDK 8
    • 对比数值上,JDK 8 –> 大对象直接进入老年代

GC日志分析_代码分析_JDK_8.png

5. GC日志分析工具

  • GCViewer
  • GCEasy
  • GCHisto
  • GCLogViewer
  • Hpjmeter
  • garbagecat

十、垃圾回收器的新发展

1. 发展概述

JDK 11

  1. Epsilon GC
    • 无操作(no-op),内存分配完成直接退出。
  2. ZGC
    • 可伸缩低延迟

Open JDK 12

  1. Shenandoah GC(实验性)
    • 第一款不是Oracle团队开发的。受到官方排挤。
    • 低停顿时间。
    • 暂停时间与堆大小无关。无论是200M还是200G,都可以把停顿时间限制到10ms以内。

2. Shenandoah GC

1) 开发团队的测试数据分析

收集器 运行时间 总停顿 最大停顿 平均停顿
Shenandoah 387.602s 320ms 89.79s 53.01ms
G1 312.052s 11.7s 1.24s 450.12ms
CMS 285.264s 12.78s 4.39s 852.26ms
Parallel Scavenge 260.092s 6.59s 3.04s 823.75ms
  • 结果是RedHat在2016年发表的论文数据,测试内容是使用ES对200G维基百科数据进行索引。从结果看:
    • 停顿时间有了质的飞跃。
    • 吞吐量方面有明显下降。
    • 总运行时间是最长的。

2) 小结

a. 优势
  • 低延迟时间。

b. 劣势
  • 高运行负担下吞吐量下降。

3. 革命性的ZGC

ZGC官方文档

  • JDK 14之前,仅支持Linux。
  • 目前mac或Windows也可以使用了。
    • -XX:+UnlockExperimentalVMOptions -XX:+UseZGC

1) 概述

a. 目标

尽可能对吞吐量影响不大的前提下,实现任意堆内存大小下都可以把垃圾收集器的停顿时间限制在10ms内。

b. 技术实现

基于Region内存布局,暂不设分代,使用读屏障染色指针内存多重映射等技术实现可并发的标记-压缩算法。

c. 过程概述

除了初始阶段是STW的,其余几乎所有地方都是并发执行的,主要分为四个阶段:

  • 并发标记;
  • 并发预备重分配;
  • 并发重分配;
  • 并发重映射。

2) 官方测试数据

a. 延迟与吞吐量对比

ZGC_官方测试数据.png

b. 停顿时间对比

ZGC_官方测试数据2.png

4. 阿里巴巴AliGC

1) 概述

  • 基于G1的算法。
  • 面向大堆(Large Heap)应用场景。

2) 对比数据

Ali_GC对比数据.png

5. Zing GC

Zing GC介绍