JVM工作原理与实战(三十七):Shenandoah GC和ZGC

简介: JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了垃圾回收器的技术演进、Shenandoah GC、ZGC等内容。

一、垃圾回收器的技术演进

垃圾回收器分为年轻代和老年代,它们各自负责不同生命周期的对象的回收。

image.gif

image.gif


不同的垃圾回收器(Garbage Collectors)在设计时,其目标呈现出显著的多样性。这些目标包括提高内存管理效率、减少程序暂停时间、优化吞吐量等。

image.gif

二、Shenandoah GC

Shenandoah GC 是由Red Hat精心研发的一款低延迟垃圾收集器。其核心优势在于并发执行大部分垃圾收集工作,包括并发的整理过程,从而极大地减少了应用程序的暂停时间。此外,Shenandoah GC 使得堆大小对STW(Stop-The-World)时间的影响微乎其微,为现代高性能、低延迟的应用程序提供了理想的解决方案。

image.gif

Shenandoah GC 的下载与构建:

鉴于Shenandoah GC仅包含在OpenJDK的特定版本中,且默认配置中并不包含,因此,用户需要手动进行构建或下载预构建的版本。

  • 架构选择:根据硬件平台,选择相应的架构,如{aarch64, arm32-hflt, Mipsel, Mips64el, ppc64le, s390x, x86_32, x86_64}。可以使用arch命令或相关工具来确定系统架构。
  • 虚拟机类型:选择server类型,它包含了所有垃圾收集器的功能,为生产环境提供了更全面的支持。
  • 优化级别:对于追求最佳性能的用户,建议选择release级别。其他如fastdebug, Slowdebug, optimization等级别则更适合于调试和优化目的。
  • 编译器版本:选择较高版本的编译器通常能带来更好的性能。然而,如果遇到兼容性问题(如程序无法启动),可能需要选择较低版本的编译器。

Shenandoah GC 的环境配置:

在使用Shenandoah GC之前,需要将OpenJDK的路径配置到系统的环境变量中。完成配置后,可以通过在命令行中运行java –version命令来验证配置是否成功。如果命令能够正常返回Java版本信息,则说明环境配置成功。

Shenandoah GC 的使用:

在配置好OpenJDK环境并安装了Shenandoah GC后,可以通过在启动Java程序时添加特定的JVM参数来使用Shenandoah GC。以下是一些关键参数:

  • -XX:+UseShenandoahGC:启用Shenandoah垃圾收集器。
  • -Xlog:gc:打印GC日志,帮助监控和分析垃圾收集活动的性能和行为。

这些参数可以在启动Java程序时使用java命令的-XX选项进行指定。例如:

# 启动一个Java应用程序,并使用Shenandoah GC进行内存管理,同时打印GC日志
java -XX:+UseShenandoahGC -Xlog:gc -jar application.jar

image.gif

三、ZGC

ZGC(Z Garbage Collector)是Java中一种高效、可扩展且低延迟的垃圾回收器。其核心优势在于能够在垃圾回收过程中将Stop-The-World(STW)时间控制在极低的范围内,通常不超过一毫秒,这使得ZGC非常适合需要低延迟和高吞吐量的应用场景。此外,ZGC支持广泛的堆大小配置,从几百兆到高达16TB,且堆大小的变化对STW时间的影响微乎其微。

通过降低垃圾回收过程中的停顿时间,ZGC能够显著降低应用程序接口的最大响应时间,从而大幅提升用户体验。然而,值得注意的是,ZGC在追求低延迟的同时,可能会牺牲一部分吞吐量。因此,如果Java服务对QPS(每秒查询次数)有较高要求,那么G1垃圾回收器可能是一个更合适的选择。

image.gif

1.ZGC的版本更迭

image.gif

2.ZGC的使用方法

ZGC在Oracle JDK和OpenJDK中均得到支持,同时阿里的DragonWell龙井JDK也提供了对ZGC的优化支持,这是基于OpenJDK 11版本的定制化改进。为了获得更佳的性能和体验,建议使用JDK 17或更高版本,这些版本在延迟优化和自动配置方面有着显著的提升,无需用户手动配置并行线程数。启用ZGC可以通过添加相应的JVM参数实现。对于分代收集,可以使用-XX:+UseZGC -XX:+ZGenerational参数启用;而对于非分代收集,则只需使用-XX:+UseZGC参数即可。

# 分代收集
-XX:+UseZGC -XX:+ZGenerational
# 非分代收集
-XX:+UseZGC

image.gif

3.ZGC的参数设置

ZGC在设计上采用了自适应策略,能够根据运行时的实际情况自动调整大部分参数,从而最小化用户需要手动配置的参数数量。例如,ZGC会自动设置年轻代的大小,无需用户通过-Xmn参数进行配置;同时,自动晋升阈值(决定对象在复制过程中存活多少次才会被移动到老年代)也无需用户通过-XX:TenuringThreshold参数进行设置。在JDK 17及更高版本中,ZGC还支持自动配置并行线程数,无需用户通过-XX:ConcGCThreads参数进行手动设置。


尽管ZGC能够自动调整大部分参数,但仍有一些关键参数需要用户根据实际情况进行设置。其中最重要的是-Xmx参数,它用于指定最大堆内存大小。由于ZGC在运行过程中会使用一部分内存来处理垃圾回收任务,因此用户需要确保堆中有足够的空间以满足应用程序的需求。-Xmx的具体设置值应根据对象分配的速度和测试情况来决定。


除了必须设置的参数外,ZGC还提供了一些可选参数供用户进行更细粒度的配置。例如,-XX:SoftMaxHeapSize参数允许用户指定一个软性最大堆内存大小。ZGC会尽量保证堆内存的使用量不超过这个值,从而在内存使用量接近这个阈值时尽早触发垃圾回收。然而需要注意的是,即使设置了-XX:SoftMaxHeapSize参数,堆内存的使用量仍有可能超过这个值。

4.ZGC的调优

为了进一步提升ZGC的性能和吞吐量,用户可以考虑使用Linux的Huge Page大页技术进行优化。通过利用大页技术,ZGC能够降低内存管理的开销并提升垃圾回收的效率。然而需要注意的是,安装和配置Huge Page需要root权限,因此ZGC默认并未开启此功能。

要使用Huge Page技术优化ZGC性能,用户可以按照以下步骤进行操作:

  1. 计算所需的大页数量。在Linux x86架构中,大页的大小通常为2MB。用户需要根据所需堆内存的大小以及JVM额外需要的非堆空间来估算所需的大页数量。例如,如果堆空间需要16GB,并预留2GB用于JVM的非堆空间,则总共需要的大页数量可以通过(16GB + 2GB) / 2MB = 9216来计算得出。
  2. 配置系统的大页池以具有所需的大页数量。这需要root权限,并且可以通过将所需的大页数量写入/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages文件来实现。例如,可以使用命令echo 9216 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages来配置系统的大页池。
  3. 在启动Java程序时添加-XX:+UseLargePages参数以启用Huge Page技术。这将使ZGC能够利用配置好的大页来提升性能和吞吐量。

通过以上步骤,用户可以利用Linux的Huge Page技术进一步优化ZGC的性能和吞吐量,从而满足更高要求的应用场景需求。

四、ZGC与Shenandoah GC的性能差异与应用场景

ZGC与Shenandoah GC均致力于实现低延迟的垃圾回收,以满足不同应用场景的需求。

设计与目标:两者均设计有高效的并行回收机制,在垃圾回收过程中会利用专门的垃圾回收线程来执行回收任务,从而有效管理内存并减少应用程序的停顿时间。

使用场景差异:

  • 内存充足场景:当系统内存资源充足时,ZGC展现出其卓越的性能。其独特的设计理念和优化策略使得它在这种情况下能够实现更短的停顿时间,为应用程序提供更为流畅的用户体验。
  • 内存受限场景:在内存资源相对紧张的情况下,Shenandoah GC则成为更优的选择。由于其优化的并行回收机制,即使在有限的内存资源下,也能实现较短的回收时间,从而确保用户请求的执行效率维持在较高水平。

ZGC和Shenandoah GC在不同的内存条件下均有其独特的优势。根据实际应用场景的内存需求,可以灵活选择适合的垃圾回收器,以优化系统性能并提升用户体验。


总结

JVM是Java程序的运行环境,负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了垃圾回收器的技术演进、Shenandoah GC、ZGC等内容,希望对大家有所帮助。

相关文章
|
3月前
|
存储 监控 Java
JVM实战—8.如何分析jstat统计来定位GC
本文详细介绍了使用jstat、jmap和jhat等工具分析JVM运行状况的方法,以及如何合理优化JVM性能。内容涵盖新生代与老年代对象增长速率、Young GC和Full GC的触发频率及耗时等关键指标的分析。通过模拟BI系统和计算系统的案例,展示了如何根据实际场景调整JVM参数以减少FGC频率,提升系统性能。最后汇总了常见问题及其解决方案,帮助开发者更好地理解和优化JVM运行状态。
JVM实战—8.如何分析jstat统计来定位GC
|
3月前
|
缓存 监控 算法
JVM实战—10.MAT的使用和JVM优化总结
本文详细探讨了JVM内存管理与性能优化的关键问题。首先分析了线上大促活动引发的老年代内存泄漏及频繁FGC问题,通过MAT工具定位到本地缓存未正确处理的原因,并提出使用Ehcache等框架解决。接着讨论了百万级数据误处理导致的频繁FGC案例,深入剖析String.split()方法在特定JDK版本下的内存消耗问题,并给出多线程并发处理大数据量的优化建议。文章还总结了JVM运行原理、GC机制以及YGC和FGC的触发条件,明确了正常系统GC频率指标。最后提供了JVM性能优化的整体思路,包括新系统开发时的参数预估、压测后的调整策略以及线上系统的监控方法,同时列举了常见的FGC原因及对应解决方案。
215 79
JVM实战—10.MAT的使用和JVM优化总结
|
3月前
|
消息中间件 缓存 Java
JVM实战—11.OOM的原因和模拟以及案例
本文详细探讨了Java系统中内存溢出(OutOfMemory,简称OOM)问题的成因与解决方法。首先分析了线上系统因OOM挂掉的常见场景及处理思路,接着深入讲解了JVM中可能发生OOM的三大区域:Metaspace(类信息存储区)、栈内存(线程执行方法时使用)和堆内存(对象存储区)。针对每个区域,文章通过具体代码示例模拟了内存溢出的情况,如动态生成过多类导致Metaspace溢出、无限递归调用引发栈内存溢出以及高负载下堆内存不足等问题。最后结合实际案例,如大数据处理系统因Kafka故障未正确处理数据缓存而导致OOM,以及无限循环调用或未缓存动态代理类引发的问题,给出了预防和改进措施。
285 64
JVM实战—11.OOM的原因和模拟以及案例
|
24天前
|
Oracle Java 关系型数据库
JVM深入原理(一+二):JVM概述和JVM功能
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行。
51 0
|
24天前
|
Arthas 存储 Java
JVM深入原理(三+四):JVM组成和JVM字节码文件
目录3. JVM组成3.1. 组成-运行时数据区3.2. 组成-类加载器3.3. 组成-执行引擎3.4. 组成-本地接口4. JVM字节码文件4.1. 字节码文件-组成4.1.1. 组成-基础信息4.1.1.1. 基础信息-魔数4.1.1.2. 基础信息-主副版本号4.1.2. 组成-常量池4.1.3. 组成-方法4.1.3.1. 方法-工作流程4.1.4. 组成-字段4.1.5. 组成-属性4.2. 字节码文件-查看工具4.2.1. javap4.2.2. jclasslib4.2.3. 阿里Arthas
35 0
|
24天前
|
存储 安全 Java
JVM深入原理(五):JVM组成和JVM字节码文件
类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析。
26 0
|
24天前
|
Arthas Java 测试技术
JVM深入原理(六)(一):JVM类加载器
目录6. JVM类加载器6.1. 类加载器-概述6.2. 类加载器-执行流程6.3. 类加载器-分类(JDK8)6.3.1. JVM底层实现的类加载器6.3.1.1. 启动类加载器6.3.2. Java代码实现类的加载器6.3.2.1. 扩展类加载器6.3.2.2. 应用程序类加载器6.4. 类加载器-Arthas查看类加载器
28 0
|
24天前
|
Java 关系型数据库 MySQL
JVM深入原理(六)(二):双亲委派机制
自定义类加载器打破双亲委派机制的方法:复写ClassLoader中的loadClass方法常见问题:要加载的类名如果是以java.开头,则会抛出安全性异常加载自定义的类都会有一个共同的父类Object,需要在代码中交由父类加载器去加载自定义类加载器不手动指定parent会默认指定应用类加载两个自定义类加载器加载同一个类会被认为是两个对象,只有相同的类加载器+想通的类限定名才会被认为是一个对象。
33 0
|
24天前
|
存储 安全 Java
JVM深入原理(七)(一):运行时数据区
栈的介绍:Java虚拟机栈采用栈的数据结构来管理方法调用中的基本数据,先进后出,每一个方法的调用使用一个栈帧来保存栈的组成:栈:一个线程运行所需要的内存空间,一个栈由多个栈帧组成栈帧:一个方法运行所需要的内存空间活动栈帧:一个线程中只能有一个活动栈帧栈的生命周期:栈随着线程的创建而创建,而回收会在线程销毁时进行栈的执行流程:栈帧压入栈内执行方法执行完毕释放内存若方法间存在调用,那么会压入被调用方法入栈,执行完后释放内存,再执行当前方法,直到执行完毕,释放所有内存。
28 0
|
24天前
|
存储 缓存 安全
JVM深入原理(七)(二):运行时数据区
堆的作用:存放对象的内存空间,它是空间最大的一块内存区域.栈上的局部变量表中,可以存放堆上对象的引用。静态变量也可以存放堆对象的引用,通过静态变量就可以实现对象在线程之间共享。堆的特点:线程共享:堆中的对象都需要考虑线程安全的问题垃圾回收:堆有垃圾回收机制,不再引用的对象就会被回收方法区的概述:方法区是存放基础信息的位置,线程共享,主要包括:类的元信息:保存了所有类的基本信息运行时常量池:保存了字节码文件中的常量池内容静态常量池:字节码文件通过编号查表的方式找到常量。
28 0