新一代虚拟机GraalVM

末蓝、 2023-09-28 08:04 125阅读 0赞

在JDK10以前,能进行性能优化的即时编译器只有C2,但C2编译器的代码据说已经变得非常庞大且臃肿,同时伴随着云原生时代的到来,Java这种需要借助JDK才能运行的语言就显得格外臃肿,于是就有了Graal编译器的诞生,Graal编译器提供了非常强悍的能力,所以通常把Graal编译器与HotSpot合称为新一代的虚拟机——GraalVM。

GraalVM是一个高性能JDK发行版,旨在加速用Java和其他语言编写的应用程序的执行,并支持JavaScript、Ruby、Python和许多其他流行语言。
在这里插入图片描述
从上面的图也可以看出来GraalVM并不是一个全新的异于HotSpot的虚拟机,而是在HotSpot的基础上,提供了功能非常强大的JIT编译器Graal Compiler。

GraalVM的设计目标是为了可以在不同的环境中运行程序,可以在JVM上运行,也可以直接将程序编译成独立的本地镜像(不需要JDK环境就能运行),或者将Java及本地代码模块集成为更大型的应用。

一、GraalVM下载和安装

GraalVM下载地址:https://www.oracle.com/downloads/graalvm-downloads.html,企业版目前也是免费的,可以使用企业版来学习使用。

选择GraalVM 20的版本,然后JDK版本选择8
6d4ee4cf390d48e1af239c6c14e5e4ec.png

在linux服务器上下载完压缩包以后,通过下面的命令解压:

  1. tar -zxf graalvm-ee-java8-linux-amd64-20.3.5.tar.gz

然后配置环境变量

  1. [root@lizhi graalvm-ee-java8-20.3.5]# vi /etc/profile
  2. JAVA_HOME=/usr/local/graalvm/graalvm-ee-java8-20.3.5
  3. PATH=/usr/local/graalvm/graalvm-ee-java8-20.3.5/bin:$PATH
  4. export JAVA_HOME PATH

刷新配置文件,使其立即生效

  1. [root@lizhi graalvm-ee-java8-20.3.5]# source /etc/profile

然后通过java -version查看

  1. [root@lizhi graalvm-ee-java8-20.3.5]# java -version
  2. java version "1.8.0_321"
  3. Java(TM) SE Runtime Environment (build 1.8.0_321-07)
  4. Java HotSpot(TM) 64-Bit Server VM GraalVM EE 20.3.5 (build 25.321-b07-jvmci-20.3-b28, mixed mode)

使用 GraalVM Enterprise,可以将 Java 字节码编译为特定于平台的、自包含的本机可执行文件(本机映像 Native Image),以实现更快的启动和更小的应用程序占用空间。

安装命令如下:

  1. [root@lizhi graalvm]# gu install native-image
  2. Downloading: Release index file from oca.opensource.oracle.com
  3. Downloading: Component catalog for GraalVM Enterprise Edition 20.3.5 on jdk8 from oca.opensource.oracle.com
  4. Skipping ULN EE channels, no username provided.
  5. Downloading: Component catalog from www.graalvm.org
  6. Processing Component: Native Image
  7. The component(s) Native Image requires to accept the following license: Oracle GraalVM Enterprise Edition Native Image License
  8. Enter "Y" to confirm and accept all the license(s). Enter "R" to the see license text.
  9. Any other input will abort installation: Y
  10. Downloading: Contents of "Oracle GraalVM Enterprise Edition Native Image License" from oca.opensource.oracle.com
  11. Downloading: Component native-image: Native Image from oca.opensource.oracle.com
  12. Installing new component: Native Image (org.graalvm.native-image, version 20.3.5)

然后通过gu list命令查看是否已经安装

  1. [root@lizhi graalvm]# gu list
  2. ComponentId Version Component name Origin
  3. --------------------------------------------------------------------------------
  4. js 20.3.5 Graal.js
  5. graalvm 20.3.5 GraalVM Core
  6. native-image 20.3.5 Native Image oca.opensource.oracle.com

二、GraalVM初体验

下面通过一个简单的程序来体验一下GraalVM的强大之处。

创建一个简单的带有main()方法的类:

  1. public class GraalVMTest {
  2. public static void main(String[] args) {
  3. System.out.println("Hello World");
  4. }
  5. }

然后编译执行

  1. [root@lizhi graalvm]# javac GraalVMTest.java
  2. [root@lizhi graalvm]# ll
  3. total 408752
  4. drwxr-xr-x 10 root root 252 May 25 17:12 graalvm-ee-java8-20.3.5
  5. -rw-r--r-- 1 root root 418550564 Mar 12 14:39 graalvm-ee-java8-linux-amd64-20.3.5.tar.gz
  6. -rw-r--r-- 1 root root 427 May 25 17:18 GraalVMTest.class
  7. -rw-r--r-- 1 root root 123 May 25 17:17 GraalVMTest.java
  8. [root@lizhi graalvm]# java GraalVMTest
  9. Hello World

下面通过native-image将其打包成一个本地镜像文件

  1. [root@lizhi graalvm]# native-image GraalVMTest
  2. [graalvmtest:1217273] classlist: 3,329.43 ms, 1.14 GB
  3. [graalvmtest:1217273] (cap): 920.18 ms, 1.62 GB
  4. [graalvmtest:1217273] setup: 4,131.87 ms, 1.62 GB
  5. [graalvmtest:1217273] (clinit): 150.62 ms, 1.71 GB
  6. [graalvmtest:1217273] (typeflow): 4,826.96 ms, 1.71 GB
  7. [graalvmtest:1217273] (objects): 3,083.11 ms, 1.71 GB
  8. [graalvmtest:1217273] (features): 173.80 ms, 1.71 GB
  9. [graalvmtest:1217273] analysis: 8,392.21 ms, 1.71 GB
  10. [graalvmtest:1217273] universe: 466.57 ms, 1.71 GB
  11. [graalvmtest:1217273] (parse): 1,589.48 ms, 1.71 GB
  12. [graalvmtest:1217273] (inline): 1,485.74 ms, 1.71 GB
  13. [graalvmtest:1217273] (compile): 17,884.23 ms, 2.08 GB
  14. [graalvmtest:1217273] compile: 21,618.90 ms, 2.08 GB
  15. [graalvmtest:1217273] image: 824.10 ms, 2.08 GB
  16. [graalvmtest:1217273] write: 195.61 ms, 2.08 GB
  17. [graalvmtest:1217273] [total]: 39,220.82 ms, 2.08 GB
  18. [root@lizhi graalvm]# ll
  19. total 412732
  20. drwxr-xr-x 10 root root 252 May 25 17:12 graalvm-ee-java8-20.3.5
  21. -rw-r--r-- 1 root root 418550564 Mar 12 14:39 graalvm-ee-java8-linux-amd64-20.3.5.tar.gz
  22. -rwxr-xr-x 1 root root 4074968 May 25 17:21 graalvmtest
  23. -rw-r--r-- 1 root root 427 May 25 17:18 GraalVMTest.class
  24. -rw-r--r-- 1 root root 123 May 25 17:17 GraalVMTest.java
  25. [root@lizhi graalvm]# ./graalvmtest
  26. Hello World

这样就生成了一个可执行文件,相比于Java的编译过程,打包成镜像文件的过程就很繁琐,即便是这样一个简单的小程序就要近一分钟的时间。这个过程就是将Java字节码编译成特性平台、自包含的本机可执行文件(本地镜像 Native Image),以实现更快的启动和更小应用程序空间。

可以通过time命令比对一下上面两种程序执行方式的区别:

  • 通过JVM来运行

    1. [root@lizhi graalvm]# time java GraalVMTest
    2. Hello World
    3. real 0m0.063s
    4. user 0m0.045s
    5. sys 0m0.020s
  • 直接执行镜像文件

    1. [root@lizhi graalvm]# time ./graalvmtest
    2. Hello World
    3. real 0m0.002s
    4. user 0m0.000s
    5. sys 0m0.002s

通过比对就可以发现,通过本地镜像启动程序,启动速度就要快很多,阿里通过这种方式加快容器的启动速度,直接将启动速度提升20倍。

最重要的一点是,这种可执行文件是不需要JDK环境的,所以可以非常便捷的完成快速的容器化部署,这才符合云原生的要求。

将镜像文件拷贝到本地的虚拟机,本地虚拟机没有安装JDK,拷贝过来后还要先授权chmod 777,然后就可以运行

  1. lizhi@Dog-li:~/test$ java -version
  2. Command 'java' not found, but can be installed with:
  3. sudo apt install default-jre
  4. sudo apt install openjdk-11-jre-headless
  5. sudo apt install openjdk-8-jre-headless
  6. lizhi@Dog-li:~/test$ ./graalvmtest
  7. Hello World

三、C2编译器与Graal编译器的区别

81f960dd13b843428d92e62644f53cbb.png

即时编译器是 Java 虚拟机中相对独立的模块,它主要负责接收 Java 字节码,并生成可以直接运行的二进制码。

但在Graal之前,以JDK8为例,即时编译器与Java虚拟机时紧耦合的,也就是说对即时编译器的更改需要重新编译整个Java虚拟机,这对于开发相对活跃的Graal来说是难以接收的。

为了让Java虚拟机与Graal解耦合,引入了Java虚拟机编译器接口(JVM Compiler Interface,JVMCI),将即时编译器的功能抽象成一个Java层面的接口。这样一来,在Graal所依赖的JVMCI版本不变的情况下,我们仅需要替换Graal 译器相关的jar包(Java 9 以后的 jmod 文件),便可完成对Graal的升级

Graal和C2最显著的区别就是:Graal是用Java写的,而C2是用C++写的。相对来说,Graal更加模块化,也更容易开发与维护,毕竟,连C2的开发者都不想去维护C2了。

Graal 的内联算法对新语法、新语言更加友好,例如 Java 8 的 lambda 表达式以及 Scala 语言。

以下面的例子来看这两者对于优化的效率:

  1. public class Test {
  2. public static void main(String[] args) {
  3. String sentence = String.join(" ",args);
  4. long total = 0L,start = System.currentTimeMillis(),last = start;
  5. for (int i = 0; i < 10; i++) {
  6. for (int j = 0; j < 10_000_000; j++) {
  7. total += sentence.chars().filter(Character::isUpperCase).count();
  8. if (j % 10_000_000 == 0){
  9. long now = System.currentTimeMillis();
  10. System.out.printf("%d (%d ms)%n",i,now-last);
  11. last = now;
  12. }
  13. }
  14. }
  15. System.out.printf("total: %d (%d ms)%n ",total,System.currentTimeMillis() -start);
  16. }
  17. }

上面这段代码循环内需要运行一千万次,会触发即时编译,所以下面通过对于在JDK8和GraalVM中运行这端代码的耗时:

JDK8:

  1. [root@zhuyuzhu test]# java -version
  2. openjdk version "1.8.0_302"
  3. OpenJDK Runtime Environment (build 1.8.0_302-b08)
  4. OpenJDK 64-Bit Server VM (build 25.302-b08, mixed mode)
  5. [root@zhuyuzhu test]# java Test
  6. 0 (74 ms)
  7. 1 (717 ms)
  8. 2 (638 ms)
  9. 3 (616 ms)
  10. 4 (607 ms)
  11. 5 (600 ms)
  12. 6 (624 ms)
  13. 7 (594 ms)
  14. 8 (597 ms)
  15. 9 (621 ms)
  16. total: 0 (6292 ms)

GraalVM:

  1. [root@lizhi graalvm]# java -version
  2. java version "1.8.0_321"
  3. Java(TM) SE Runtime Environment (build 1.8.0_321-07)
  4. Java HotSpot(TM) 64-Bit Server VM GraalVM EE 20.3.5 (build 25.321-b07-jvmci-20.3-b28, mixed mode)
  5. [root@lizhi graalvm]# java Test
  6. 0 (62 ms)
  7. 1 (124 ms)
  8. 2 (448 ms)
  9. 3 (16 ms)
  10. 4 (16 ms)
  11. 5 (15 ms)
  12. 6 (16 ms)
  13. 7 (15 ms)
  14. 8 (16 ms)
  15. 9 (15 ms)
  16. total: 0 (759 ms)

从运行结果可以明显的看出来,Graal即时编译器的优化能力要比C2强很多。

发表评论

表情:
评论列表 (有 0 条评论,125人围观)

还没有评论,来说两句吧...

相关阅读

    相关 一代虚拟GraalVM

    在JDK10以前,能进行性能优化的即时编译器只有C2,但C2编译器的代码据说已经变得非常庞大且臃肿,同时伴随着云原生时代的到来,Java这种需要借助JDK才能运行的语言就显得格

    相关 一代图形技术WebGL

    WebGL,是一项用来在网页上绘制和渲染图形(3D图形),并允许用户进行交互的技术。在传统意义上,通常只有高配置的计算机才能渲染图形。但是现在,随着个人计算机和浏览器的性能越来