JVM工作原理与实战(三十六):GraalVM虚拟机

本文涉及的产品
函数计算FC,每月15万CU 3个月
简介: JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了GraalVM、GraalVM的两种运行模式、GraalVM应用场景、参数优化和故障诊断等内容。

一、GraalVM介绍

GraalVM是由Oracle官方推出的一款高性能Java开发工具包(JDK),与OpenJDK和Oracle JDK相比,它为用户提供了更出色的性能。GraalVM标语为“Build faster, smaller, leaner applications”(更快、更小、更精简的应用程序开发)。GraalVM具有以下特点:

  • 低CPU和内存使用率:与传统的JDK相比,GraalVM在执行应用程序时可以显著降低CPU和内存的使用率,从而提高应用程序的性能。

image.gif

  • 快速启动和预热:GraalVM能够实现快速的应用程序启动,并且无需预热即可获得最佳性能,从而缩短了应用程序的启动时间。

image.gif

  • 更好的安全性:GraalVM提供了更高级别的安全性,可以更好地保护应用程序免受攻击和数据泄露等安全威胁。
  • 小型可执行文件:通过优化和压缩技术,GraalVM可以生成更小的可执行文件,从而减少了应用程序的存储空间占用。
  • 支持多种框架:GraalVM支持多种流行的Java框架,如Spring Boot、Micronaut、Helidon和Quarkus,使得开发人员可以更加方便地构建和部署应用程序。
  • 云平台支持:GraalVM已经得到了多家云平台的支持,使得开发人员可以轻松地将应用程序部署到云环境中。
  • 多语言支持:通过Truffle框架,GraalVM可以运行JavaScript、Python、Ruby等多种其他语言,从而扩展了其应用范围。


GraalVM的官方网址:https://www.graalvm.org/


GraalVM分为社区版(Community Edition)和企业版(Enterprise Edition)。与社区版相比,企业版在性能方面进行了更多的优化和改进,提供了更高级别的功能和特性。建议在开发高性能、安全可靠的应用程序时使用企业版GraalVM。

特性 描述 社区版 企业版
收费 是否收费 免费 收费
G1垃圾回收器 使用G1垃圾回收器优化垃圾回收性能 ×
Profile Guided Optimization(PGO) 运行过程中收集动态数据,进一步优化本地镜像的性能 ×
高级优化特性 更多优化技术,降低内存和垃圾回收的开销 ×
高级优化参数 更多的高级优化参数可以设置 ×

二、GraalVM的两种运行模式

JIT(Just-In-Time)模式(即时编译模式)

JIT模式是GraalVM的一种核心特性,其处理方式与Oracle JDK的JIT编译器类似。在这种模式下,GraalVM的即时编译器将Java字节码转换为本地机器码,以实现更高的执行效率。其主要特点如下:

  • 一次编写,到处运行(Write Once, Run Anywhere):这是Java的核心承诺,意味着开发者可以使用GraalVM在任何支持Java的平台或环境中编译和运行他们的代码。
  • 热点代码优化:预热之后,GraalVM的即时编译器会针对频繁执行的代码段进行优化,生成比传统的Hotspot JIT更高的性能机器码。

image.gif

AOT(Ahead-Of-Time)模式(提前编译模式)

AOT模式是GraalVM的另一项创新,它允许开发人员在编译阶段就生成特定平台的可执行文件。这意味着应用程序在启动时已经预先编译为本地代码,从而实现了更高的启动速度和运行性能。其主要特点如下:

  • 本地可执行文件生成:通过AOT编译器,开发人员可以为特定平台(如Windows、Linux或macOS)生成可执行文件(如.exe、.app或.out)。
  • 启动后达到最高性能:由于应用程序在启动时已经转换为本地代码,因此无需进行额外的编译或优化,可以立即达到最佳性能。
  • 非跨平台编译:与JIT模式不同,AOT模式下生成的可执行文件不具备跨平台特性。每个目标平台都需要单独编译。
  • 本地镜像(Native Image):AOT模式下生成的文件被称为本地镜像,它们是为特定平台和配置预先编译的应用程序映像。

image.gif

在选择使用GraalVM的哪种模式时,开发人员应考虑他们的应用程序需求、目标平台以及对性能、跨平台兼容性和安全性的要求。JIT模式提供了高度的跨平台兼容性和动态优化能力,而AOT模式则专注于预先编译的高性能和特定平台的可执行文件。

GraalVM模式和版本的性能对比:

image.gif

三、GraalVM应用场景

1.GraalVM存在的问题

GraalVM的AOT模式虽然带来了启动速度、内存和CPU开销上的优势,但也存在一些问题需要解决:

  • 跨平台问题:由于AOT模式下的代码是针对特定平台编译的,因此在使用不同平台运行时需要进行多次编译。这增加了开发人员的工作量,并需要确保编译平台的依赖库等环境与运行平台保持一致。
  • 编译时间与资源消耗:使用AOT模式编译本地镜像时,需要较长时间,并且会消耗大量的CPU和内存资源。这可能对开发环境和持续集成/持续部署(CI/CD)流程造成影响。
  • 运行时类访问限制:AOT编译器在编译时需要知道运行时所有可访问的类。然而,Java中存在一些技术可以在运行时动态创建类,例如反射、动态代理等。这些技术在许多框架中广泛使用,如Spring框架。因此,这些框架需要与AOT编译器进行适配,以确保其功能的正常使用。

针对上述问题,以下是一些可能的解决方案:

  • 使用容器化平台:利用公有云的Docker等容器化平台进行在线编译,可以确保编译环境和运行环境的一致性。这样不仅解决了跨平台问题,还解决了编译资源问题。通过容器化技术,可以轻松地在不同的环境中构建和部署应用程序。
  • 采用整合了GraalVM AOT模式的框架版本:例如,使用Spring Boot 3等框架版本,这些框架已经与GraalVM AOT模式进行了深度整合,简化了适配过程,并可能提供了更好的性能和功能支持。

2.GraalVM企业级应用-Serverless架构

在传统的系统架构中,企业需要自行负责服务器等基础设施的运维、安全和高可用性等方面的工作,这带来了两个主要问题:

  • 开销巨大:这包括了人力成本的开销和机房建设的开销。企业需要配备专门的技术团队进行基础设施的管理和维护,同时还需要投入大量的资源进行机房建设和设备采购。
  • 资源浪费:面对流量冲击,例如秒杀等促销活动,企业必须提前规划并准备大量的服务器资源。然而,在活动结束后,这些服务器可能会处于闲置状态,导致资源的严重浪费。

为了解决这些问题,随着虚拟化技术和云原生技术的不断成熟,云服务商开始提供Serverless无服务器化的架构。这种架构使得企业无需进行服务器的任何配置和部署,完全由云服务商负责提供和管理基础设施。Serverless架构能够根据应用程序的实际需求动态地分配资源,确保应用程序的高可用性和弹性扩展。

在Serverless架构中,企业可以将应用程序部署到云服务商提供的平台上,并利用云服务商提供的API和事件驱动的机制来编写业务逻辑。这样,企业可以专注于应用程序的开发和业务逻辑的实现,而无需关心基础设施的管理和维护。比较典型的Serverless服务提供商包括亚马逊AWS和阿里云等。这些云服务商提供了丰富的Serverless服务,如函数计算、数据库、存储和消息队列等,使得企业可以更加便捷地构建和部署Serverless应用程序。通过采用Serverless架构,企业可以降低开销、减少资源浪费,并实现应用程序的高可用性和弹性扩展。同时,与GraalVM的结合使用可以进一步提高应用程序的性能和安全性,为企业提供更加高效和可靠的解决方案。

3.Serverless架构-函数计算

在Serverless架构中,函数计算是一种常见的服务模式,它将应用程序拆分为多个独立的函数,每个函数都以事件驱动的方式运行。这种模式的核心思想是将应用程序的逻辑分解为一系列独立的、可复用的函数,从而提高了应用程序的可维护性和可扩展性。函数计算的服务提供商包括亚马逊AWS的Lambda和阿里云的FC等。这些服务提供商提供了全面的函数计算解决方案,使开发人员能够专注于编写和部署函数逻辑,而无需关心底层基础设施的管理和维护。

函数计算的主要应用场景包括:

  • 小程序和API服务中的接口:对于调用频率较低的接口,使用常规的服务器架构可能会导致资源浪费。通过使用Serverless架构,开发人员可以实现按需付费,降低成本,同时利用自动伸缩功能应对流量的突发情况。
  • 大规模任务处理:对于需要处理大量数据或执行复杂计算的任务,例如音视频文件转码、审核等,可以利用事件机制在文件上传后自动触发相应的任务。函数计算可以提供高效的资源利用和弹性扩展能力,以满足大规模任务处理的需求。

在计费方面,函数计算通常基于CPU和内存使用量进行计费。因此,使用GraalVM AOT模式编译本地镜像可以进一步降低成本。通过使用GraalVM AOT编译器,开发人员可以生成高效的本地镜像,从而减少运行函数所需的CPU和内存资源,进而降低函数计算的计费成本。

4.Serverless架构-Serverless应用

在Serverless架构中,除了函数计算服务外,云服务商还提供了基于容器的Serverless应用作为另一种解决方案。这种方案针对函数计算服务的局限性,为需要更长时间运行或更大资源需求的应用程序提供了支持。

基于容器的Serverless应用的核心优势在于其自动化的容器管理。与传统的手动配置Kubernetes中的Pod和Service等内容不同,基于容器的Serverless应用简化了这一过程。开发人员只需选择所需的镜像,云服务商即可自动生成应用服务,无需进行繁琐的配置和管理。这种方案适用于各种应用场景,特别是需要长时间运行或大量资源支持的应用。例如,一些批处理任务、大数据处理和分析、实时流数据处理等场景,都可以通过基于容器的Serverless应用实现高效的处理和扩展。

在计费方面,基于容器的Serverless应用通常也基于CPU和内存使用量进行计费。因此,使用GraalVM AOT模式编译本地镜像可以进一步降低成本。通过使用GraalVM AOT编译器,开发人员可以生成高效的本地镜像,从而减少运行应用所需的CPU和内存资源,进而降低基于容器的Serverless应用的计费成本。

四、参数优化和故障诊断

在GraalVM中,由于它是一款独立的JDK,许多在HotSpot虚拟机中常用的参数在这里并不适用。为了确保最佳的性能和稳定性,需要对GraalVM的参数进行细致的调整和优化。以下是一些关键的参数和故障诊断建议:

内存管理参数:

  • 最大堆大小(Max Heap Size): 社区版GraalVM默认使用串行垃圾回收器(Serial GC),其最大Java堆大小默认设置为物理内存的80%。如需调整,可以使用-Xmx参数来设置最大堆大小。如果希望在编译期就指定该大小,可以在编译时添加-R:MaxHeapSize=最大堆大小参数。
  • G1垃圾回收器: 企业版GraalVM提供了G1垃圾回收器,它能有效降低垃圾回收的延迟。开启方式为添加--gc=G1参数。
  • Epsilon GC: 对于生命周期短暂且不产生大量对象的程序,可以考虑使用Epsilon GC。它不会产生任何垃圾回收行为,从而没有额外的内存和CPU开销。开启方式为添加--gc=epsilon参数。

垃圾回收日志与诊断:

  • 使用-XX:+PrintGC-XX:+VerboseGC参数可以打印出垃圾回收的详细信息,这对于故障诊断和性能调优非常有帮助。

性能优化时可以指定的一小部分选项:

-H:G1HeapRegionSize(只能在映像构建时指定) - G1 区域的大小。
-XX:MaxRAMPercentage- 如果未另行指定最大堆大小,则用作最大堆大小的物理内存大小的百分比。
-XX:MaxGCPauseMillis- 最长暂停时间的目标。
-XX:ParallelGCThreads- 在垃圾回收暂停期间用于并行工作的最大线程数。
-XX:ConcGCThreads- 用于并发工作的最大线程数。
-XX:InitiatingHeapOccupancyPercent- 触发标记周期的 Java 堆占用阈值。
-XX:G1HeapWastePercent- 候选集合集中允许的未回收空间。如果候选收集组中的可用空间低于该值,则 G1 将停止空间回收阶段。

image.gif

其他常用的参数参考:

https://www.graalvm.org/22.3/reference-manual/native-image/optimizations-and-performance/MemoryManagement/


总结

JVM是Java程序的运行环境,负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了GraalVM、GraalVM的两种运行模式、GraalVM应用场景、参数优化和故障诊断等内容,希望对大家有所帮助。

相关文章
|
4月前
|
Oracle Java 关系型数据库
JVM深入原理(一+二):JVM概述和JVM功能
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行。
125 0
|
4月前
|
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
89 0
|
4月前
|
存储 安全 Java
JVM深入原理(五):JVM组成和JVM字节码文件
类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析。
63 0
|
4月前
|
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查看类加载器
72 0
|
4月前
|
Java 关系型数据库 MySQL
JVM深入原理(六)(二):双亲委派机制
自定义类加载器打破双亲委派机制的方法:复写ClassLoader中的loadClass方法常见问题:要加载的类名如果是以java.开头,则会抛出安全性异常加载自定义的类都会有一个共同的父类Object,需要在代码中交由父类加载器去加载自定义类加载器不手动指定parent会默认指定应用类加载两个自定义类加载器加载同一个类会被认为是两个对象,只有相同的类加载器+想通的类限定名才会被认为是一个对象。
172 0
|
4月前
|
存储 安全 Java
JVM深入原理(七)(一):运行时数据区
栈的介绍:Java虚拟机栈采用栈的数据结构来管理方法调用中的基本数据,先进后出,每一个方法的调用使用一个栈帧来保存栈的组成:栈:一个线程运行所需要的内存空间,一个栈由多个栈帧组成栈帧:一个方法运行所需要的内存空间活动栈帧:一个线程中只能有一个活动栈帧栈的生命周期:栈随着线程的创建而创建,而回收会在线程销毁时进行栈的执行流程:栈帧压入栈内执行方法执行完毕释放内存若方法间存在调用,那么会压入被调用方法入栈,执行完后释放内存,再执行当前方法,直到执行完毕,释放所有内存。
69 0
|
4月前
|
存储 缓存 安全
JVM深入原理(七)(二):运行时数据区
堆的作用:存放对象的内存空间,它是空间最大的一块内存区域.栈上的局部变量表中,可以存放堆上对象的引用。静态变量也可以存放堆对象的引用,通过静态变量就可以实现对象在线程之间共享。堆的特点:线程共享:堆中的对象都需要考虑线程安全的问题垃圾回收:堆有垃圾回收机制,不再引用的对象就会被回收方法区的概述:方法区是存放基础信息的位置,线程共享,主要包括:类的元信息:保存了所有类的基本信息运行时常量池:保存了字节码文件中的常量池内容静态常量池:字节码文件通过编号查表的方式找到常量。
63 0
|
前端开发 Java 应用服务中间件
【Java虚拟机】JVM类加载机制和双亲委派模型
【Java虚拟机】JVM类加载机制和双亲委派模型
【Java虚拟机】JVM类加载机制和双亲委派模型
|
安全 Java 应用服务中间件
深入理解JVM虚拟机6:深入理解JVM类加载机制
深入理解JVM类加载机制 简述:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
|
3月前
|
虚拟化 数据中心
VMware vSphere Replication 9.0.3 - 虚拟机复制和数据保护
VMware vSphere Replication 9.0.3 - 虚拟机复制和数据保护
157 0