当前位置: 首页 > news >正文

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 jbbJava Business Benchmark)基准测试是标准性能评 估组织 SPEC 发布的一项用于衡量服务器 Java 应用性能的测试基 准。从 2000 6 SPEC 组织发布第一版服务器 Java 性能测试基 SPEC jbb2000 至今,历经了 SPEC jbb20052006.1-2013.10)、 SPEC jbb20132013.1-2014.12)多个版本的更新后,SPEC jbb2015

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

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

  •  图 1:几种垃圾回收器 SPEC jbb2015 测试结果

  • 图 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(服务水平协议)10ms25ms50ms75ms 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:+ UseConcMarkSweepGCCMS 回收 器 )、- XX:+UseG1GCG1 回收 器 )、-XX:+UseZGCZGC),运行 SPEC jbb2015 测 试,并使用参数 -XX:+PrintGCDetails-Xlog:gc. log 输出垃圾回收详细日志

1 反映了几种垃圾回收器在 SPEC jbb2015 基准测试中的 性能表现,可以看出在极限性能指标 Max-jOPS 上,并行回收器 ParallelOldGC 表现最好,这是由于 Max-jOPS 代表系统极限吞吐量 而并行回收器在所有垃圾回收器中最侧重于吞吐量,因此这一指标 明显优于其他垃圾回收器。而在关键性能指标上,ZGC 的表现最 好,证明在一些有特定响应时间要求的业务场景下,其综合性能最 好。ZGC 由于低停顿时间的特性,每次垃圾回收停顿时间不会超 10ms,这要远远低于其他垃圾回收方式(见图 2)。一般来说, 停顿时间的减少但带来的影响必然是吞吐量的降低,ZGC 垃圾回 收处理工作都在与应用线程并发执行,同时也会不可避免地占用很 多 CPU 并发工作导致吞吐量降低。但如果同时考虑低响应时间与 吞吐量情况下,ZGC 吞吐量降低的程度在可接受的范围之内,但 同时其停顿时间减少了一个量级,因此其综合性能表现更好。

2 展示了几种垃圾回收方式的平均停顿时间。其中 ZGC 的停顿时间最短,看到 ZGC 虽然目标定位在停顿时 间不超过 10ms,但在实际测试时 ZGC 平均停顿时间约为 2ms,要 远远低于 10 毫秒的目标。串行回收器的停顿时间最长,几乎达到 秒级,这是由于串行回收器回收时只有单线程,因此停顿的时间要 远远高于其他垃圾回收器,因此在实际业务模型中性能很差。而并 行回收器、G1 回收器平均停顿时间相差不大,都在 100ms 上下, CMS 回收器则平均在 200ms 左右。

4 结语

本文简述了几种常用的 JVM 垃圾回收方式及其原理,并利用 业内权威的 Java 应用性能测试工具 SPEC jbb2015 测试了几种垃 圾回收方式的实际性能。因为 SPEC jbb2015 测试模型则具有广泛 的代表性,所以对基于此项测试对垃圾回收的研究有助于开发者 在实际应用中合理地对性能进行调优并解决性能问题。根据 SPEC jbb2015 测试结果来看,当侧重于追求最大吞吐量,如基于 Java 的后台计算型应用、事务处理时,并行垃圾回收器 ParallelOldGC 表现更好。如追求低停顿时间,快速响应如互联网应 用、web 前端等时 ZGC 优势明 显,或者也可以使用 G1 垃圾回收器配 -XX:MaxGCPauseMillis 参数来限制最大垃圾回收停顿时间。且 ZGC 所支持的超大内存也非常适合需要大量内存的应用程序,比如大数据应用程序。但由于实际业务性能考量标准不同,还需结合 每种垃圾回收方式自身特点,合理进行选择。

相关文章:

  • flink笔记2(Flink 快速上手)
  • k8s部署服务+日志收集+监控系统+CICD自动化
  • Docker Compose
  • 【大厂面试重点(程序环境和预处理以及C语言不能函数重载但C++却可以的原理分析)】
  • 【Google】“共码未来“—2022谷歌开发者大会参会记录
  • C/C++恶意代码盘点丨自动删除功能
  • C#使用winform做一个开关小游戏
  • Golang仿ps获取Linux进程信息
  • 24.0、 C语言——自定义数据类型_位段
  • 基于Python实现的Alpha-Beta剪枝算法
  • 【芯片前端】可能是定向验证的巅峰之作——auto_testbench
  • 【JAVA】JUnit单元测试、类加载器、反射、注解
  • 如何搭建mysql的主从关系
  • 基于Java毕业设计养老院管理系统源码+系统+mysql+lw文档+部署软件
  • 26.分页
  • JavaScript-如何实现克隆(clone)函数
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • axios 和 cookie 的那些事
  • Fastjson的基本使用方法大全
  • flask接收请求并推入栈
  • Flex布局到底解决了什么问题
  • Hexo+码云+git快速搭建免费的静态Blog
  • Java,console输出实时的转向GUI textbox
  • JavaScript创建对象的四种方式
  • JS数组方法汇总
  • js作用域和this的理解
  • 欢迎参加第二届中国游戏开发者大会
  • ------- 计算机网络基础
  • 精彩代码 vue.js
  • 看域名解析域名安全对SEO的影响
  • 人脸识别最新开发经验demo
  • 思否第一天
  • 微信公众号开发小记——5.python微信红包
  • 与 ConTeXt MkIV 官方文档的接驳
  • nb
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • Nginx实现动静分离
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • 数据可视化之下发图实践
  • ​渐进式Web应用PWA的未来
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (ZT)薛涌:谈贫说富
  • (二)JAVA使用POI操作excel
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (接口自动化)Python3操作MySQL数据库
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (三十五)大数据实战——Superset可视化平台搭建
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (一)Linux+Windows下安装ffmpeg
  • .NET 8.0 发布到 IIS
  • .net core IResultFilter 的 OnResultExecuted和OnResultExecuting的区别
  • .net 提取注释生成API文档 帮助文档