天天看点

JVM - 垃圾回收方式性能研究

本文从几种 JVM 垃圾回收方式及原理出发,研究了在基准测试中不同垃圾回收方式对于 JVM 性能的影响,并 通过最终测试数据对比,给出了不同应用场景下如何选择垃圾回收策略的方法

垃圾回收(Garbage Collection, GC)是 Java 虚拟机(JVM)中 使用的一种内存管理方案,它能够不断自动释放内存中的不再被使 用的对象,并按照特定的垃圾回收算法实现对于内存资源的管理。 相较于 C/C++ 手动内存管理方式,GC 的出现大大减少了开发人 员在内存资源管理方面的工作,是 JVM 的核心组成部分,并且对 JVM 的性能有着重要的影响。

随着技术及硬件的发展,新的垃圾回收方式也在随着 JDK 版 本的更新而被加入,如在 JDK1.7 中正式引入了 G1 回收器,JDK 11 中引入了 Z 回收器等。面对如此多的垃圾回收器,如何在不同 实际应用场景下中选择最适合的是进行 JVM 性能优化的首要问题, 解决这一问题则必须先了解不同垃圾回收方式的性能表现。以往也 有相关方面的研究,但性能测试或基于 SPEC JVM2008 又或是基 于 SPEC jbb2000 等一些现已不再提供支持的测试工具,已不能 适应当前时代的需求,而通过一种权威且最新的基准测试工具分析 不同垃圾回收器的性能表现是十分有意义的。

SPEC jbb(Java Business Benchmark)基准测试是标准性能评 估组织 SPEC 发布的一项用于衡量服务器 Java 应用性能的测试基 准。从 2000 年 6 月 SPEC 组织发布第一版服务器 Java 性能测试基 准 SPEC jbb2000 至今,历经了 SPEC jbb2005(2006.1-2013.10)、 SPEC jbb2013(2013.1-2014.12)多个版本的更新后,SPEC jbb2015

(2015.9- 至今)基于最新的 Java 应用程序特性开发,支持虚拟化和云环境。现在 SPEC jbb 测试已经发展成为 JVM 厂商、Java 开发 者,研究学者,以及相关学术机构评估 Java 业务应用性能及可扩 展性的一项权威基准测试标准 。

本文将首先从垃圾回收原理出发,分别介绍几种垃圾回收器, 然后对这几种垃圾回收器在基准测试中的性能表现进行分析。

JVM - 垃圾回收方式性能研究
  • 图 1:几种垃圾回收器 SPEC jbb2015 测试结果
JVM - 垃圾回收方式性能研究
  • 图 2:几种垃圾回收器平均停顿时间比较

1 垃圾回收算法

Java 自动化的管理内存资源必须通过垃圾回收算法来确定哪些 是有效的对象,哪些是无效的对象,对于无效的对象就要进行垃圾 回收处理。作为垃圾回收器的实现基础,下面先介绍常见的垃圾回 收算法。

1.1 标记清除法

在垃圾回收算法中,根是指向对象的指针的起点部分。通过根 对象进行引用搜索,最终可达的对象被称为可达对象;通过根对象 进行引用搜索,最终没有被引用的对象被称为不可达对象。在标记阶段,首先通从根节点开始标记所有的可达对象,不可达对象则为 垃圾对象。在清除阶段,清除所有未被标记的对象。由于清除时不 考虑内存空间的连续性,因此标记清除法最大的问题是产生内存空 间碎片。特别是在进行大对象内存分配时,相较于连续内存空间, 内存碎片的存在会降低堆内存效率。

1.2 复制算法

复制算法将内存空间划分为相等的两部分,且每次只会只使用其中的一部分,当垃圾回收时,首先将使用中那部分内存里存活的 对象复制到另一部分内存区,接着清除使用的内存块中的所有对象, 最后将两个内存区进行互换,从而完成垃圾回收。复制算法在复制 的过程中能够有效避免内存空间碎片的产生,但代价是系统内存空 间损失一半。

1.3 标记压缩法

标记压缩法是一种针对老年代的垃圾回收算法。对于老年代, 由于大部分对象都是存活对象,使用复制算法代价将使内存折半, 成本太高并不适用。标记压缩法正是为了应对这种情况而产生,首 先从根节点开始,对所有可达对象进行标记,然后将所有存活的对 象压缩到内存一端,再清理剩余的所有的空间。这种方法既避免了 碎片的产生,又不需要将内存分为两半,效率很高。

1.4 分区算法

分区算法将整个堆空间划分成连续的不同小区间,每一小区间 都独立使用,独立回收,并可以控制一次回收的小区间数量。一般 来说,在相同条件下,堆空间越大,进行一次垃圾回收的所需的时 间就越长,导致产生的停顿时间也越长。因此如果将堆内存分割成 多个小块,并根据目标停顿时间的要求,每次合理地控制回收部分 小区间,而不是回收整个堆空间,则可以有效减小一次垃圾回收所 产生的的停顿时间。

2 几种垃圾回收器

垃圾收集算法可以看作 Java 虚拟机内存回收的抽象策略,而 垃圾收集器则是其内存回收的具体实现。Java 虚拟机中,垃圾回收 器不只一种,在不同的应用场景下如何选择性能最佳的垃圾回收器 是需要有清楚认识的,下面介绍几种常见的垃圾回收器。

2.1 串行回收器

串行回收器是一种单线程垃圾回收器,在进行每次垃圾回收时, 只有一个线程工作,Java 应用程序中的其他所有线程暂停,等待垃 圾回收完成(“Stop-The-World”过程)。在实时性要求高的应用 场景下,往往会造成用户体验不佳。串行回收器在新生代中使用复 制算法,老年代中使用标记压缩法。

2.2 并行回收器

并行回收器在串行回收器的基础上做了改进,它使用多个线程 同时进行垃圾回收,可以有效减少垃圾回收所需的时间。新生代中 使用复制算法,老年代使用标记压缩算法。并行回收器关注系统的 吞吐量,可以通过 -XX:MaxGCPauseMills 和 -XX:GCTimeRatio 控 制的垃圾回收最大停顿时间和吞吐量。但须指出的是,减少一次收 集的最大停顿时间,就会同时减少系统吞吐量,增大系统吞吐量又 可能会同时增加一次垃圾回收的最大停顿时间。

2.3 CMS回收器

CMS 回收器使用标记清除算法,利用多线程并行回收,主要 侧重于系统停顿时间。CMS 工作时首先经过初始标记与并发标记来标记出需要回收的对象、通过预清理做清理前的准备及控制停顿 时间、再经过重新标记修正并发标记的数据、最后进行并发清除, 以及并发重置为下次回收做准备。

2.4 G1回收器

G1 回收器可以视为 CMS 回收器的替代品,它使用了独特的分 区算法,相比于之前介绍的垃圾回收器将堆内存划分为固定内存大 小的年轻代、老代和永久代(JDK1.8 后被元空间取代),G1 回收 器将堆分割成一组大小相等的区域,每个区域是一个连续的虚拟内 存范围,某些区域被功能与年轻代老年代相同,但是它们没有固定 的大小,这为内存使用提供了更大的灵活性。在进行垃圾回收时, G1 回收器可以只选择部分区域,且部分垃圾回收工作能与 Java 应 用程序并行,提高回收效率的同时相应降低停顿时间。G1 回收器 虽然也使用标记清除法,但与 CMS 不同的是 G1 可以有效复制移 动对象,消除了潜在的内存碎片问题。此外,G1 还允许用户自行 设定所需的暂停时间。

2.5 Z回收器

ZGC 是从 JDK11 中引入的一种新的支持弹性伸缩的低延迟垃 圾收集器,主要实现了三大目标:停顿时间不超过 10ms、停顿时 间不随堆或实时设置的大小而增加、支持从 8MB 到 16TB 的堆内存。 ZGC 的一个核心设计原则是使用读屏障(load barrier)和着色指针(colored pointer)。在 Java 中加载对象的行为会受到读屏障的影响, 而着色指针具有供读屏障使用的信息,它使 ZGC 能够查找、标记、 定位和重新映射对象,这有助于降低垃圾回收的开销并极大降低停 顿时间,且对吞吐量影响最大不超过 15%。作为一个并发的垃圾 收集器,ZGC 所有的工作都是在 Java 应用程序线程执行时完成的, 这极大地减少了垃圾回收对应用程序响应时间的影响。而且 Z 回收 器现在还处于持续开发阶段,后续的开发目标是达到垃圾回收停顿 时间不超过 1ms。

3 垃圾回收器性能比较

SPEC jbb2015 测试模拟了一个典型的商业应用的三层架构环境 中的中间层工作,包含商业逻辑、对象操作等,目的是衡量服务器 Java 应用之性能。模型建立在一个全球型连锁超市的 IT 基础架构 之上,通过线上线下购物、库存管理、供应链管理、用户购买行为 的数据挖掘等业务来评估整个系统的吞吐量及响应时间随着整个系 统业务量不断增加时的性能表现。SPECjbb2015 支持多种测试运 行配置、支持虚拟化以及云环境,使用户能够全面分析和解决可能 存在于包括硬件、操作系统、JVM 和应用程序层的性能瓶颈问题。 SPEC jbb2015 测试结果包含主要包含两个侧式指标 Max-jOPS 和 Critical-jOPS。最大性能指标 Max-jOPS 是系统最大每秒钟处 理的 Java 操作数,可以看做在业务响应不失败的情况下,服务器 的极限吞吐量, 反映的是系统极限 Java 应用性能。 关键性能指 标 Critical-jOPS 是系统在 5 个关键 SLA(服务水平协议)10ms, 25ms,50ms,75ms 和 100ms 响应时间下平均每秒 Java 操作数。

选择这些点是为了保证不同行业使用的响应时间目标的合理分布, 可以看做衡量的是在响应时间有限的情况下的系统吞吐量。

测试环境选择使用四路服务器,配置 4 颗 Intel Xeon Platinum 8180 CPU,物理内存大小为 1536G,操作系统为 Red Hat Enterprise Linux 7.6,使用 JDK 版本为 Oracle Java SE 13.0.2,在此环境下分 别使用 -XX:+UseSerialGC(串行回收器)、-XX:+UseParallelOldGC(并 行回收 器 )、-XX:+ UseConcMarkSweepGC(CMS 回收 器 )、- XX:+UseG1GC(G1 回收 器 )、-XX:+UseZGC(ZGC),运行 SPEC jbb2015 测 试,并使用参数 -XX:+PrintGCDetails、-Xlog:gc. log 输出垃圾回收详细日志。

4 结语