GraalVM 虚拟机

Graal 编译器以及由此诞生的GraalVM,虽然目前还处在实验阶段,但是也是 Java 程序员们必须要了解的,因为他未来极有可能替代 HotSpot,成为 Java生态的下一代技术基础。

1 、关于 Graal

Graal编译器最早是作为 HotSpot 的 C1 编译器的下一代编译器设计的,使用 Java 语言进行编写。2012 年,Graal编译器才发展成为一个独立的 Java 编译项目。而早期的 Graal其实也和 C1,C2 一样,需要与 HotSpot 虚拟机配合工作。但是随着 JDK9 开始推出 JVMCI(Java虚拟机编译器接口),才让 Graal 可以从 HotSpot 中独立出来,并逐渐形成了现在的 GraalVM 。

虽然你可能对 Graal 了解不多,但是,Graal 其实一直深受业界关注。Oracle 公司希望他能够发展成为一个更完美的编译器,款高编译效率、高输出质量、支持提前编译和即时编译,同时支持应用于包括HotSpot在内的不同虚拟机。而使用 C \C++编写的 C1 和 C2 编译器,也逐渐变得越来越臃肿,维护和更新都更加困难。这时使用 Java 语言编写的 Graal 自然就成了首选 。

另外,在业务层面,Java 也急需一种更高效的编译器来迎合现在越来越火爆的云原生架构。现在作为 Java 主流的服务端版本总体上是面向大规模,长时间运行的系统设计的。像即时编译器(JIT)、性能优化、垃圾回收等有代表性的特征都是面向程序长时间运行设计的,需要一段时间预热才能达到最佳性能,才能享受硬件规模提升带来的红利。但是现在的微服务背景下,对服务的规模以及稳定性要求在逐渐降低,反而对容器化、启动速度、预热时间等方面提出了新的要求。而这方面都是 Java的弱项。因此 Java 语言也需要这样一款新的虚拟机,来提升与很多新出来的现代语言,比如golang的竞争优势。

2 、使用 GraalVM

接下来使用 GraalVM 就比较简单了。在 GraalVM 的官方文档中,首先有一段对于 GraalVM 的整体描述:

从这段整体描述就能看到,使用 GraalVM 和使用其他的 JDK,没有什么大的区别。所以,使用 GraalVM 的方式也是 下载-》配置环境变量-》编译-》执行 几个步骤。

GraalVM 的官网地址是:https://www.graalvm.org 。官网上目前就可以下载对应版本的产品。当前有 Java17 和 Java21两个版本。

下载下来后是一个tar包压缩文件。接下来跟安装jdk一样,解压,配置JAVA_HOME 环境变量,就可以用java -version进行测试了。这部分就略过了。比如我安装后的结果是这样的

[oper@localhost ~]$ java -version
java version "17.0.9" 2023-10-17 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 17.0.9+11.1 (build 17.0.9+11-LTS-jvmci-23.0-b21)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.9+11.1 (build 17.0.9+11-LTS-jvmci-23.0-b21, mixed mode, sharing)
​

另外,在 GraalVM 中还提供了一个管理指令 gu

[oper@localhost ~]$ gu list
ComponentId              Version             Component name                Stability                     Origin 
---------------------------------------------------------------------------------------------------------------------------------
graalvm                  23.0.2              GraalVM Core                  Supported                     
native-image             23.0.2              Native Image                  Early adopter    
​

接下来,写一个简单的 Java 代码进行测试。Hello.java

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}
​

使用 javac 编译成 Hello.class文件。然后使用java Hello进行执行。使用time指令看到执行的时间如下:

[oper@localhost ~]$ time java Hello
Hello World!
​
real    0m0.062s
user    0m0.067s
sys     0m0.011s
​

接下来,切换成 Oracle 的 JDK17,也同样执行这个程序,做个简单的对比。结果如下:

然后,GraalVM 还提供了一个功能,可以将 Class 文件直接编译成本地镜像,这些本地镜像不需要 JVM 虚拟机也能直接运行。这就是 Graal 的 AOT 提前编译。

但是我在编译这个简单的 Hello 时,却遇到了意想不到的错误:

[oper@localhost ~]$ native-image Hello
========================================================================================================================
GraalVM Native Image: Generating 'hello' (executable)...
========================================================================================================================
[1/8] Initializing...                                                                                    (3.4s @ 0.09GB)
 Java version: 17.0.9+11-LTS, vendor version: Oracle GraalVM 17.0.9+11.1
 Graal compiler: optimization level: 2, target machine: armv8-a, PGO: ML-inferred
 C compiler: gcc (redhat, aarch64, 11.4.1)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
[2/8] Performing analysis...  [****]                                                                     (7.6s @ 0.24GB)
   1,831 (59.26%) of  3,090 types reachable
   1,733 (46.69%) of  3,712 fields reachable
   7,726 (35.98%) of 21,471 methods reachable
     623 types,     0 fields, and   285 methods registered for reflection
      49 types,    32 fields, and    48 methods registered for JNI access
       4 native libraries: dl, pthread, rt, z
[3/8] Building universe...                                                                               (1.0s @ 0.25GB)
[4/8] Parsing methods...      [**]                                                                       (2.6s @ 0.22GB)
[5/8] Inlining methods...     [***]                                                                      (0.6s @ 0.21GB)
[6/8] Compiling methods...    [****]                                                                    (16.6s @ 0.25GB)
[7/8] Layouting methods...    [*]                                                                        (0.4s @ 0.39GB)
[8/8] Creating image...       [*
]                                                                        (0.0s @ 0.27GB)
------------------------------------------------------------------------------------------------------------------------
                        2.7s (8.1% of total time) in 146 GCs | Peak RSS: 0.95GB | CPU load: 1.95
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 /home/oper/svm_err_b_20231129T171758.715_pid2504.md (build_info)
========================================================================================================================
Failed generating 'hello' after 33.1s.
​
The build process encountered an unexpected error:
​
> java.lang.RuntimeException: There was an error linking the native image: Linker command exited with 1
​
Linker command executed:
/usr/bin/gcc -z noexecstack -Wl,--gc-sections -Wl,--version-script,/tmp/SVM-6698742675696986223/exported_symbols.list -Wl,-x -o /home/oper/hello hello.o /home/oper/graalvm-jdk-17.0.9/lib/svm/clibraries/linux-aarch64/liblibchelper.a /home/oper/graalvm-jdk-17.0.9/lib/static/linux-aarch64/glibc/libnet.a /home/oper/graalvm-jdk-17.0.9/lib/static/linux-aarch64/glibc/libnio.a /home/oper/graalvm-jdk-17.0.9/lib/static/linux-aarch64/glibc/libjava.a /home/oper/graalvm-jdk-17.0.9/lib/static/linux-aarch64/glibc/libfdlibm.a /home/oper/graalvm-jdk-17.0.9/lib/svm/clibraries/linux-aarch64/libjvm.a -Wl,--export-dynamic -v -L/tmp/SVM-6698742675696986223 -L/home/oper/graalvm-jdk-17.0.9/lib/static/linux-aarch64/glibc -L/home/oper/graalvm-jdk-17.0.9/lib/svm/clibraries/linux-aarch64 -lz -lpthread -ldl -lrt
​
Linker command output:
使用内建 specs。
COLLECT_GCC=/usr/bin/gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/aarch64-redhat-linux/11/lto-wrapper
目标:aarch64-redhat-linux
配置为:../configure --enable-bootstrap --enable-host-pie --enable-host-bind-now --enable-languages=c,c++,fortran,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-plugin --enable-initfini-array --without-isl --enable-multilib --with-linker-hash-style=gnu --enable-gnu-indirect-function --build=aarch64-redhat-linux --with-build-config=bootstrap-lto --enable-link-serialization=1
线程模型:posix
Supported LTO compression algorithms: zlib zstd
gcc 版本 11.4.1 20230605 (Red Hat 11.4.1-2) (GCC) 
COMPILER_PATH=/usr/libexec/gcc/aarch64-redhat-linux/11/:/usr/libexec/gcc/aarch64-redhat-linux/11/:/usr/libexec/gcc/aarch64-redhat-linux/:/usr/lib/gcc/aarch64-redhat-linux/11/:/usr/lib/gcc/aarch64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/aarch64-redhat-linux/11/:/usr/lib/gcc/aarch64-redhat-linux/11/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/aarch64-redhat-linux/11/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-z' 'noexecstack' '-o' '/home/oper/hello' '-v' '-L/tmp/SVM-6698742675696986223' '-L/home/oper/graalvm-jdk-17.0.9/lib/static/linux-aarch64/glibc' '-L/home/oper/graalvm-jdk-17.0.9/lib/svm/clibraries/linux-aarch64' '-mlittle-endian' '-mabi=lp64' '-dumpdir' '/home/oper/hello.'
 /usr/libexec/gcc/aarch64-redhat-linux/11/collect2 -plugin /usr/libexec/gcc/aarch64-redhat-linux/11/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/aarch64-redhat-linux/11/lto-wrapper -plugin-opt=-fresolution=/tmp/cc7AWuPB.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -dynamic-linker /lib/ld-linux-aarch64.so.1 -X -EL -maarch64linux -o /home/oper/hello -z noexecstack /usr/lib/gcc/aarch64-redhat-linux/11/../../../../lib64/crt1.o /usr/lib/gcc/aarch64-redhat-linux/11/../../../../lib64/crti.o /usr/lib/gcc/aarch64-redhat-linux/11/crtbegin.o -L/tmp/SVM-6698742675696986223 -L/home/oper/graalvm-jdk-17.0.9/lib/static/linux-aarch64/glibc -L/home/oper/graalvm-jdk-17.0.9/lib/svm/clibraries/linux-aarch64 -L/usr/lib/gcc/aarch64-redhat-linux/11 -L/usr/lib/gcc/aarch64-redhat-linux/11/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/aarch64-redhat-linux/11/../../.. --gc-sections --version-script /tmp/SVM-6698742675696986223/exported_symbols.list -x hello.o /home/oper/graalvm-jdk-17.0.9/lib/svm/clibraries/linux-aarch64/liblibchelper.a /home/oper/graalvm-jdk-17.0.9/lib/static/linux-aarch64/glibc/libnet.a /home/oper/graalvm-jdk-17.0.9/lib/static/linux-aarch64/glibc/libnio.a /home/oper/graalvm-jdk-17.0.9/lib/static/linux-aarch64/glibc/libjava.a /home/oper/graalvm-jdk-17.0.9/lib/static/linux-aarch64/glibc/libfdlibm.a /home/oper/graalvm-jdk-17.0.9/lib/svm/clibraries/linux-aarch64/libjvm.a --export-dynamic -lz -lpthread -ldl -lrt -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/aarch64-redhat-linux/11/crtend.o /usr/lib/gcc/aarch64-redhat-linux/11/../../../../lib64/crtn.o
/usr/bin/ld: 找不到 -lz
collect2: 错误:ld 返回 1
​
Please inspect the generated error report at:
/home/oper/svm_err_b_20231129T171758.715_pid2504.md
​
If you are unable to resolve this problem, please file an issue with the error report at:
https://graalvm.org/support
​

经分析,这是因为我当前服务器上缺少zlib库导致。所以需要先安装zilb库

# root权限安装zlib库
sudo yum install zlib-devel
​

之后再重新编译,就可以编译出一个可以执行的hello应用程序。这个应用程序不需要JDK也能正常运行,并且执行速度也更快。

[oper@localhost ~]$ java -version
bash: java: command not found...
Install package 'java-11-openjdk-headless' to provide command 'java'? [N/y] n
​
​
[oper@localhost ~]$ time ./hello
Hello World!
​
real    0m0.006s
user    0m0.000s
sys     0m0.006s
​

稍有曲折,完成了第一次GraalVM的体验。从这个过程中可以简单看出,GraalVM的这种AOT编译模式,能够极大提升Java程序的执行速度,更贴合现在的微服务,云原生的技术环境。所以,或许不久的将来,深入理解GraalVM有可能成为每个java程序员的必修课。

而从当前的官网文档中,可以看到 GraalVM 更为强大之处。

基于这个 Truffle 框架,未来完全可以开发出各种语言的翻译器,这样,其他一些常用的语言也可以在 GraalVM 上执行。想象一下,未来js ,python,php, lua等等这些语言都可以在 GraalVM 上执行,再加上这种本地镜像的执行方式,会是一种什么样的景象?

所以,不要再说 Java 没落了,Java 未来可期,你我共同期待!!!

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐