过两年 JVM 可能就要被它替代了

news2024/11/25 21:20:06

今天说一说 GraalVM。

GraalVM 是 Oracle 大力发展和想要推广的新一代 JVM ,目前很多框架都已经渐渐支持 GraalVM 了,比如我们在用的 Spring 也已经推出了对 GraalVM 兼容的工具包了。

既然说的这么厉害,那么它到底是何方神圣呢。

GraalVM 和 JVM 的关系

既然叫做VM,那肯定和 JVM 有关系的吧。JVM 全称 Java 虚拟机,我们都知道,Java 程序是运行在虚拟机上的,虚拟机提供 Java 运行时,支持解释执行和部分的(JIT)即时编译器,并且负责分配和管理 Java 运行所需的内存,我们所说的各种垃圾收集器都工作在 JVM 中。

比如 Oracle JDK、OpenJDK ,默认的 JVM 是 HotSpot 虚拟机,这是当前应用最广泛的一个虚拟机。我们平时见到的各种将虚拟机的书籍、文章、面试题,基本上都是说的 HotSpot 虚拟机。

除此之外,还有一些商用,或者说小众的虚拟机存在,比如IBM 的J9 JVM,商用的 Zing VM 等。

那 GraalVM 是另一种 Java 虚拟机吗?

是,又不全是。

GraalVM 可以完全取代上面提到的那几种虚拟机,比如 HotSpot。把你之前运行在 HotSpot 上的代码直接平移到 GraalVM 上,不用做任何的改变,甚至都感知不到,项目可以完美的运行。

但是 GraalVM 还有更广泛的用途,不仅支持 Java 语言,还支持其他语言。这些其他语言不仅包括嫡系的 JVM 系语言,例如 Kotlin、Scala,还包括例如 JavaScript、Nodejs、Ruby、Python 等。

GraalVM 的野心不止于此,看上面的图,它的目的是搭建一个 Framework,最终的目标是想要支持任何一种语言,无论哪种语言,可以共同跑在 GraalVM 上,不存在跨语言调用的壁垒。

GraalVM 和JDK有什么关系

Java 虚拟机都是内置在 JDK 中的,比如Orcale JDK、OpenJDK,默认内置的都是 HotSpot 虚拟机。

GraalVM 也是一种 JDK,一种高性能的 JDK。完全可以用它替代 OpenJDK、Orcale JDK。

GraalVM 如何运行 Java 程序

说了半天,是不是还是不知道 GraalVM 到底是什么。

  • GraalVM - 还包含 Graal (JIT)即时编译器,可以结合 HotSpot 使用

  • GraalVM – 是一种高性能 JDK,旨在加速 Java 应用程序性能,同时消耗更少的资源。

  • GraalVM - 是一种支持多语言混编的虚拟机程序,不仅可以运行 JVM 系列的语言,也可支持其他语言。

GraalVM 提供了两种方式来运行 Java 程序。

第一种:结合 HotSpot 使用

上面说了,GraalVM 包含 Graal (JIT)即时编译器,自从 JDK 9u 版本之后,Orcale JDK 和 OpenJDK 就集成了 Graal 即时编译器。我们知道 Java 既有解释运行也有即时编译。

当程序运行时,解释器首先发挥作用,代码可以直接执行。随着时间推移,即时编译器逐渐发挥作用,把越来越多的代码编译优化成本地代码,来获取更高的执行效率。即时编译器可以选择性地编译热点代码,省去了很多编译时间,也节省很多的空间。比如多次执行的方法或者循环、递归等。

JDK 默认使用的是 C2 即时编译器,C2是用C++编写的。而使用下面的参数可以用 Graal 替换 C2。

-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler
复制代码

Graal 编译器是用 Java 实现的,用 Java 实现自己的编译器。Graal 基于一些假设的条件,采取更加激进的方式进行优化。采用 Graal 编译器之后,对性能有会有一定的提升。

但是如果你还是在用 JDK8,那对不起了,GraalVM 的一切都用不了。

第二种:AOT 编译本地可执行程序

这是 GraalVM 真正厉害的地方。

AOT 提前编译,是相对于即时编译而言的。AOT在运行过程中耗费 CPU 资源来进行即时编译,而程序也能够在启动的瞬间就达到理想的性能。例如 C 和 C++语言采用的是AOT静态编译,直接将代码转换成机器码执行。而 Java 一直采用的是解释 + 即时编译技术,大多数情况下 Java 即时编译的性能并不比静态编译差,但是还是一直朝着 AOT 编译的方向努力。

但是 Java 对于 AOT 来说有一些难点,比如类的动态加载和反射调用。

GraalVM 显然是已经克服了这些问题,使用 GraalVM 可以直接将 Java 代码编译成本地机器码形态的可执行程序。

我们目前运行 Java 一定要安装 JDK 或者 JRE 对不对,如果将程序直接编译成可执行程序,就不用在服务器上安装 JDK 或 JRE 了。那就是说运行 Java 代码其实也可以不用虚拟机了是吗?

GraalVM 的 AOT 编译实际上是借助了 SubstrateVM 编译框架,可以将 SubstrateVM 理解为一个内嵌精简版的 JVM,包含异常处理,同步,线程管理,内存管理(垃圾回收)和 JNI 等组件。

SubstrateVM 的启动时间非常短,内存开销非常少。用这种方式编译出的 Java 程序的执行时间可与C语言持平。

下图是使用即时编译(JVM运行)与 AOT (原生可执行程序)两种方式的 CPU 和内存使用情况对比,可以看出来,AOT 方式下 CPU 和内存的使用都非常少。

除了运行时占用的内存少之外,用这种方式最终生成的可执行文件也非常小。这对于云端部署非常友好。目前很多场景下都使用 Docker 容器的方式部署,打一个 Java 程序的镜像包要包含完整的 JVM 环境和编译好的 Jar 包。而AOT 方式可以最大限度的缩小 Docker 镜像的体积。

缺点

好处多多,当然也有一些弊端。对于反射这种纯粹在运行时才能确定的部分,不可能完全通过优化编译器解决,只能通过增加配置的方式解决。麻烦是麻烦了一点,但是是可行的,Spring Boot 2.7的版本已经支持原生镜像了,Spring 这种非常依赖反射的框架都可以支撑,我们用起来也应该没问题。

GraalVM 如何支持多语言

要支持多语言,就要说到 GraalVM 中的另一个核心组件 Truffle 了。

Truffle 是一个用 Java 写就的语言实现框架。基于 Truffle 的语言实现仅需用 Java 实现词法分析、语法分析以及针对语法分析所生成的抽象语法树(Abstract Syntax Tree,AST)的解释执行器,便可以享用由 Truffle 提供的各项运行时优化。

就一个完整的 Truffle 语言实现而言,由于实现本身以及其所依赖的 Truffle 框架部分都是用 Java 实现的,因此它可以运行在任何 Java 虚拟机之上。

当然,如果 Truffle 运行在附带了 Graal 编译器的 Java 虚拟机之上,那么它将调用 Graal 编译器所提供的 API,主动触发对 Truffle 语言的即时编译,将对 AST 的解释执行转换为执行即时编译后的机器码。

目前除了 Java, JavaScript、Ruby、Python 和许多其他流行语言都已经可以运行在 GraalVM 之上了。

GraalVM 官方还提供了完整的文档,当有一天你开发了一款新的语言,也可以用 Truffle 让它跑在 GraalVM 上。

安装和使用

GraalVm 目前的最新版本是 22.3,分为社区版和企业版,就好像 OpenJDK 和 商用的 Orcale 的 JDK ,企业版会多一些性能分析的功能,用来帮助更大程度的优化性能。

社区版是基于OpenJDK 11.0.17, 17.0.5, 19.0.1,而商业版基于Oracle JDK 8u351, 11.0.17, 17.0.5, 19.0.1,所以,如果你想用免费的,只能将程序升级到 JDK 11 以上了。

GraalVM 支持 Windows、Linux、MacOS ,可以用命令安装最新版,或者直接下载对应 Java 版本的。

我是下载的 Java 11 的版本,下载下来的压缩包,直接解压,然后配置环境变量。把解压目录配置到环境变量的 JAVA_HOME就可以了。

解压好其实就相当于安装完毕了,查看一下版本。

进入到解压目录下的bin目录中,运行 java -version。运行结果如下:

运行代码

常用方式运行

也就是我们平时一直在用的这种方式,把 GrralVM 当做 OpenJDK 使用,只不过把即时编译器换成了 Graal 。就是前面说的第一种方式。

安装完成后,就可以把它当做正常的 JDK 使用了,比如 javacjpsjmap等都可以直接用了。大多数人还是用 IDEA 的,所以就直接在 IDEA 中使用就好了。

1、先随意创建一个 Java 项目。

2、创建完成后,打开项目设置。

3、在打开的项目设置弹出框中选择 SDKs,点击加号,选择前面解压的 GraalVM 目录。

4、然后选择刚刚添加的这个 JDK。

5、最后运行一段测试代码。

public class HelloWorld {
    public static void main(String[] args) throws Exception {
        System.out.println("Hello GraalVM!");
        Thread.sleep(1000 * 100 * 100);
    }
}
复制代码

上面这样的运行方式,其实就相当于前面说的第一种运行方式

native-image 方式运行

这种方式就是 AOT 编译成机器码,已可执行文件的形式出现。native-image 可以命令行的形式执行,也可以在配合 Maven 执行,我这儿就直接演示用 Maven 形式的了,毕竟IDEA 搭配 Maven 用习惯了。

1、安装native-image 工具包

native-image 是用来进行 AOT 编译打包的工具,先把这个装上,才能进行后面的步骤。

安装好 GraalVM 后,在 bin目录下有一个叫做 gu的工具,用这个工具安装,如果将 bin目录添加到环境中,直接下面的命令安装就行了。

gu install native-image
复制代码

如果没有将 bin目录加到环境变量中,要进入到 bin目录下,执行下面的命令安装。

./gu install native-image
复制代码

这个过程可能比较慢,因为要去 github 上下载东西,如果一次没成功(比如超时),多试两次就好了。

2、配置 Maven

配置各种版本

 <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>${java.specification.version}		</maven.compiler.source>
    <maven.compiler.target>${java.specification.version}</maven.compiler.target>
    <native.maven.plugin.version>0.9.12</native.maven.plugin.version>
    <imageName>graalvm-demo-image</imageName>
    <mainClass>org.graalvm.HelloWorld</mainClass>
  </properties>
复制代码

native.maven.plugin.version是要用到的编译为可执行程序的 Maven 插件版本。

imageName是生成的可执行程序的名称。

mainClass是入口类全名称。

配置 build 插件

  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.0.0</version>
        <executions>
          <execution>
            <id>java-agent</id>
            <goals>
              <goal>exec</goal>
            </goals>
            <configuration>
              <executable>java</executable>
              <workingDirectory>${project.build.directory}</workingDirectory>
              <arguments>
                <argument>-classpath</argument>
                <classpath/>
                <argument>${mainClass}</argument>
              </arguments>
            </configuration>
          </execution>
          <execution>
            <id>native</id>
            <goals>
              <goal>exec</goal>
            </goals>
            <configuration>
              <executable>${project.build.directory}/${imageName}</executable>
              <workingDirectory>${project.build.directory}</workingDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <source>${maven.compiler.source}</source>
          <target>${maven.compiler.source}</target>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.2.2</version>
        <configuration>
          <archive>
            <manifest>
              <addClasspath>true</addClasspath>
              <mainClass>${mainClass}</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <archive>
            <manifest>
              <addClasspath>true</addClasspath>
              <mainClass>${mainClass}</mainClass>
            </manifest>
          </archive>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
      </plugin>

    </plugins>

  </build>
复制代码

配置 profiles

  <profiles>
    <profile>
      <id>native</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.graalvm.buildtools</groupId>
            <artifactId>native-maven-plugin</artifactId>
            <version>${native.maven.plugin.version}</version>
            <extensions>true</extensions>
            <executions>
              <execution>
                <id>build-native</id>
                <goals>
                  <goal>build</goal>
                </goals>
                <phase>package</phase>
              </execution>
              <execution>
                <id>test-native</id>
                <goals>
                  <goal>test</goal>
                </goals>
                <phase>test</phase>
              </execution>
            </executions>
            <configuration>
              <fallback>false</fallback>
              <buildArgs>
                <arg>-H:DashboardDump=fortune -H:+DashboardAll</arg>
              </buildArgs>
              <agent>
                <enabled>true</enabled>
                <options>
                  <option>experimental-class-loader-support</option>
                </options>
              </agent>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
复制代码

3、使用 maven 编译,打包成本地可执行程序。

执行 Maven 命令

mvn clean package
复制代码

或者

mvn  -Pnative -Dagent package 
复制代码

编译打包的过程比较慢,因为要直接编译成机器码,所以比一般的编译过程要慢一些。看到下面的输入日志,说明打包成功了。

4、运行可执行程序包,打开 target 目录,已经看到了graalvm-demo-image可执行程序包了,大小为 11.58M。

然后就可以运行它了,进入到目录下,执行下面的命令运行,可以看到正常输出了。注意了,这时候已经是没有用到本地 JVM 了。

./graalvm-demo-image 
Hello GraalVM!
复制代码

这时候,用 jps -l命令已经看不到这个进程了,只能通过 ps看了。

总结

虽然我们还没有看到有哪个公司说在用 GraalVM 了,但是 QuarkusSpring BootSpring等很多的框架都已经支持 GraalVM 的 Native-image 模式,而且在 Orcale 的大力推广下,相信不久之后就会出现在更多的产品中。赶紧体验一下吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/90462.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java零基础入门-Scanner类

目录 1.概念 2.常用api 3.next()方法 4.nextLine()方法 5.next()与nextLine()区别 6.实例 1.概念 针对java5版本诞生&#xff0c;在jdk之前版本这次的版本是添加了java.util.Scanner类&#xff1b;其类是一个用于扫描输入文本的工具集。它不仅提供了可结合正则表达式和从输…

基于云原生的集群自愈系统 Flink Cluster Inspector

作者: 舟柒、楼台 1. 业务背景与挑战 1.1 实时计算集群现状 关于热点机器处理一直是阿里云 Flink 集群运维的一大痛点&#xff0c;不管在日常还是大促都已经是比较严重的问题&#xff0c;同时这也是分布式系统的老大难问题。而在今年整个阿里云成本控制的背景下&#xff0c;…

Flink 1.16:Hive SQL 如何平迁到 Flink SQL

摘要&#xff1a;本文整理自 Apache Flink PMC&Committer 伍翀&#xff08;云邪&#xff09;在 9 月 24 日 Apache Flink Meetup 的演讲。主要内容包括&#xff1a;Hive SQL 迁移的动机Hive SQL 迁移的挑战Hive SQL 迁移的实践Hive SQL 迁移的演示未来规划Tips&#xff1a;…

班级网页制作 HTML个人网页设计 我的班级网站设计与实现 大学生简单班级静态HTML网页设计作品 DIV布局班级网页模板代码 DW学生校园网站制作成品下载

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

【数据库数据恢复】SQL server数据库被加密怎么恢复数据?

SQL server数据库故障&#xff1a; SQL server数据库和备份文件被加密&#xff0c;无法使用。数据库MDF、LDF、log日志文件名字被修改。 SQL server数据库数据恢复过程&#xff1a; 1、首先对故障数据库所涉及到的硬盘进行镜像备份&#xff0c;避免对原始数据造成二次破坏&…

python大作业高分项目--射击闯关游戏

项目功能&#xff1a; 地图编辑器&#xff1a;可以实现玩家自己定义每一关卡的样式和难易程度 运行界面&#xff1a;实现了玩家的移动&#xff0c;跳跃&#xff0c;发射子弹&#xff0c;投掷手雷&#xff0c;以及敌人的AL&#xff08;移动&#xff0c;发射子弹&#xff0c;扔…

Word处理控件Aspose.Words功能演示:在 Python 中将 HTML 转换为 PNG、JPEG、BMP、GIF 或 TIFF 图像

Aspose API支持流行文件格式处理&#xff0c;并允许将各类文档导出或转换为固定布局文件格式和最常用的图像/多媒体格式。 HTML &#xff08;超文本标记语言&#xff09;是所有浏览器都支持的主要网页文件格式。它经常用于将数据和信息显示为网页。在某些情况下&#xff0c;我们…

迎接工业互联网的龙卷风暴,软通动力绘制了一张转型地图

《绿野仙踪》一书的开始&#xff0c;主角多萝西被一股龙卷风卷起来&#xff0c;从此离开了平凡无奇的堪萨斯州&#xff0c;来到神奇的奥兹国。这种让人一步登天、进入仙境的“龙卷风暴”&#xff0c;也在科技行业不停上演。在微型计算机和个人电脑PC这两场大型龙卷风市场中&…

cdr最新软件下载2023中文版电脑64位免费安装包

CorelDRAW Graphics Suite2023涵盖了全部CorelDRAW图形处理组件&#xff0c;是一款智能高效的平面设计软件&#xff0c;广泛应用于排版印刷、矢量图形编辑及网页设计等领域&#xff0c;30多年来无数优秀的设计师通过CorelDRAW大胆展现真我&#xff0c;交付了出众的创意作品&…

web前端期末大作业——仿小米商城电商平台(6页) html+css+javascript网页设计实例 企业网站制作

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 ⚽精彩专栏推荐&#x1…

【面试】RabbitMQ面试题答案整理

1、RabbitMQ routing路由模式 1、 消息生产者将消息发送给交换机按照路由判断,路由是字符串(info) 当前产生的消息携带路由字符(对象的方法),交换机根据路由的key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息; 2、 根据业务功能定义路由字符串 3、 从系统的…

关于IPv6升级改造的政策文件汇总-中科三方

一、《推进互联网协议第六版&#xff08;IPv6&#xff09;规模部署行动计划》 发布时间&#xff1a;2017年11月 发文单位&#xff1a;中共中央办公厅、国务院办公厅 主要内容&#xff1a;用5到10年时间&#xff0c;形成下一代互联网自主技术体系和产业生态&#xff0c;建成全…

Matlab论文插图绘制模板第70期—带误差棒的柱状图(Bar with Errorbar)

在之前的文章中&#xff0c;分享了一系列Matlab柱状图的绘制模板&#xff1a; 这一次&#xff0c;再来分享一种特殊的柱状图&#xff1a;带误差棒的柱状图。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;Matlab论文插图绘制模板系列&#xff0c;旨在降低大家使用Matl…

直播倒计时 1 天 | SOFAChannel#31 RPC 框架设计的考和量

SOFARPC 是蚂蚁集团开源的一款基于 Java 实现的 RPC 服务框架&#xff0c;为应用之间提供远程服务调用能力&#xff0c;具有高可伸缩性&#xff0c;高容错性&#xff0c;目前蚂蚁集团所有的业务的相互间的 RPC 调用都是采用 SOFARPC。SOFARPC 为用户提供了负载均衡&#xff0c;…

全网唯一,不忽悠的ChatGPT

Datawhale干货 作者&#xff1a;Ben&#xff0c;中山大学&#xff0c;Datawhale成员最近ChatGPT火出圈了&#xff0c;它和前阵子的Stable Diffusion&#xff08;AIGC&#xff09;一样成为社交媒体上人们津津乐道的话题。“ChatGPT要取代谷歌搜索了&#xff1f;”“ChatGPT要让程…

制作 Python Docker 镜像的最佳实践

概述 &#x1f4da;️Reference: 制作容器镜像的最佳实践 这篇文章是关于制作 Python Docker 容器镜像的最佳实践。(2022 年 12 月更新&#xff09; 最佳实践的目的一方面是为了减小镜像体积&#xff0c;提升 DevOps 效率&#xff0c;另一方面是为了提高安全性。希望对各位有所…

无忧·企业文档,为企业客户的场景而思考

作为无忧企业文档的设计者&#xff0c;常常被问到一个问题&#xff0c;这个和腾X文档有什么区别&#xff1f;其实这个问题我口头回答了很多次&#xff0c;这次&#xff0c;我将这个问题做个记录与分析。 主要是有以下几个方面不同&#xff1a; 1、客户定位不同 2、核心功能不…

Java基础疑难点梳理(泛型到反射9章内容)

文章目录1. 泛型2. 基础类库3. 集合4. 异常5. 注解6. JDBC7. IO流8. 网络编程9. 类加载和反射1. 泛型 静态方法的形参&#xff0c;静态变量&#xff0c;静态代码块中不能使用泛型&#xff08;因为即使泛型不同&#xff0c;还是同一个类&#xff0c;静态变量是属于类的&#xff…

锂电池实验室规划设计方案 | 喜格SICOLAB

锂电池实验室规划设计方案 | 喜格SICOLAB 锂电池生产基本工序 正负极混料、涂布、辊压、干燥、裁切、卷绕或叠片、封装、注液、化成、检测、出货等。锂电池实验室设计标准 1、《电子工业洁净厂房设计规范》GB50472-2008 2、《锂离子电池工厂设计标准》GB 51377-2019 《工业建筑…

在 Python 中使用 cv2 进行嗜睡检测

大家好&#xff0c;在这个博客中&#xff0c;我们将构建一个嗜睡检测应用程序&#xff0c;它将检测视频中的人是否变得昏昏欲睡。这是一个非常有趣且简单的项目&#xff0c;代码甚至不到 80 行&#xff0c;让我们开始吧看看最终输出注意——你不会在这里听到警报声&#xff0c;…