大家好,我是Kite。今天我们来谈谈GraalVM。GraalVM是甲骨文大力发展并欲推广的新一代JVM。目前很多框架已经逐渐支持GraalVM。比如我们现在使用的Spring也推出了兼容GraalVM的工具包。既然都说如此厉害,那它到底有何神圣之处呢?既然GraalVM和JVM的关系叫做VM,那肯定和JVM有关系。JVM的全称是JavaVirtualMachine。众所周知,Java程序运行在虚拟机上。虚拟机提供Java运行时,支持解释执行和一些(JIT)即时编译器,负责Java运行时的分配和管理。内存,我们正在谈论的各种垃圾收集器都在JVM中工作。比如OracleJDK、OpenJDK,默认的JVM是HotSpot虚拟机,是目前使用最广泛的虚拟机。我们平时看到的关于虚拟机的各种书籍、文章、面试题,基本都是HotSpot虚拟机。此外,还有一些商业的或者小众的虚拟机,比如IBM的J9JVM,商业的ZingVM等。那么GraalVM是不是又一个Java虚拟机呢?是的,不完全是。GraalVM可以完全替代上面提到的虚拟机,比如HotSpot。将你之前在HotSpot上运行的代码直接翻译到GraalVM上,不做任何改动,甚至意识不到,项目就可以完美运行。但GraalVM的用途更广,不仅支持Java语言,其他语言也同样支持。这些其他语言不仅包括Kotlin、Scala等直接的JVM语言,还包括JavaScript、Nodejs、Ruby、Python等,GraalVM的野心不止于此。看上图,它的目的是搭建一个Framework。最终的目标是支持任何语言,无论是哪种语言,都可以在GraalVM上一起运行,并且没有跨语言调用障碍。GraalVM和JDK有什么关系Java虚拟机都是JDK内置的,比如OracleJDK、OpenJDK,默认内置HotSpot虚拟机。GraalVM也是一个JDK,一个高性能的JDK。它可以完全替代OpenJDK和OracleJDK。GraalVM是如何运行Java程序的,已经讲了很久了,但我还是不知道GraalVM是什么。GraalVM-还包含Graal(JIT)即时编译器,可与HotSpot结合使用GraalVM-是一种高性能JDK,旨在加速Java应用程序性能,同时消耗更少的资源。GraalVM-是一个支持多语言混合的虚拟机程序。不仅可以运行JVM系列的语言,还支持其他语言。GraalVM提供了两种运行Java程序的方式。第一个:与HotSpot结合使用如上所述,GraalVM包含Graal(JIT)即时编译器。从JDK9u版本开始,OracleJDK和OpenJDK都集成了Graal即时编译器。我们知道Java既有解释运行时,也有即时编译。程序运行时,解释器先发挥作用,直接执行代码即可。随着时间的推移,即时编译器逐渐发挥作用,将越来越多的代码编译优化成本地代码,以获得更高的执行效率。即时编译器可以选择性地编译热代码,节省了大量的编译时间和空间。比如多次执行的方法或者循环、递归等。JDK默认使用C2即时编译器,而C2是用C++编写的。相反,使用以下参数将C2替换为Graal。-XX:+UnlockExperimentalVMOptions-XX:+UseJVMCICompilerGraal编译器是用Java实现的,用Java实现自己的编译器。Graal基于一些假设采用更积极的优化方法。使用Graal编译器后,性能会有一定的提升。但如果你还在使用JDK8,抱歉,GraalVM的一切都将无法工作。第二种:AOT编译本地可执行程序这就是GraalVM真正强大的地方。AOT提前编译,与即时编译相反。AOT在运行过程中消耗CPU资源进行即时编译,程序在启动的一瞬间就能达到理想的性能。比如C、C++语言使用AOT静态编译,直接将代码转换成机器码执行。Java一直采用解释+即时编译技术。大多数情况下,Java即时编译的性能并不比静态编译差,只是一直在向AOT编译的方向努力。但是Java对于AOT有一些难点,比如类的动态加载和反射调用。GraalVM显然已经克服了这些问题。使用GraalVM,可以将Java代码以本地机器码的形式直接编译成可执行程序。我们现在必须要安装JDK或者JRE才能运行Java,对吧?如果程序直接编译成可执行程序,则不需要在服务器上安装JDK或JRE。也就是说,其实不用虚拟机也可以运行Java代码,对吧?GraalVM的AOT编译其实就是使用了SubstrateVM编译框架。SubstrateVM可以理解为一个嵌入式精简的JVM,包括异常处理、同步、线程管理、内存管理(垃圾收集)和JNI等组件。SubstrateVM具有非常快的启动时间和非常小的内存开销。这样编译的Java程序的执行时间可以与C语言相当。下图是使用即时编译(JVM运行)和AOT(本机可执行程序)的CPU和内存使用率对比。可以看出,AOT模式下CPU和内存的占用非常少。除了在运行时使用更少的内存之外,这种方式生成的可执行文件也非常小。这对于云部署非常友好。目前很多场景都部署了Docker容器,一个Java程序镜像包中必须包含完整的JVM环境和编译好的Jar包。AOT方式可以最小化Docker镜像的大小。有很多缺点和优点,但当然也有一些缺点。对于反射这部分可以纯粹在运行时确定的部分,通过优化编译器是不可能完全解决的,只能通过增加配置来解决。麻烦是有点麻烦,但是可行。SpringBoot2.7版本已经支持原生镜像。Spring这种严重依赖反射的框架都能支持,我们使用起来应该没有问题。GraalVM如何支持多语言要支持多语言,就要说说GraalVM中的另一个核心组件Truffle。Truffle是一个用Java编写的语言实现框架。基于Truffle的语言实现只需要使用Java实现词法分析、语法分析,以及语法分析生成的抽象语法树(AbstractSyntaxTree,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和商业OracleJDK一样,企业版会有更多的性能分析功能,帮助更大程度的优化性能。社区版基于OpenJDK11.0.17、17.0.5、19.0.1,商业版基于OracleJDK8u351、11.0.17、17.0.5、19.0.1,所以如果想免费使用,您只能使用升级到JDK11或更高版本的程序。GraalVM支持Windows、Linux和MacOS。可以通过命令安装最新版本,也可以直接下载对应的Java版本。我下载的是Java11版本,下载压缩包,直接解压,然后配置环境变量。将解压目录配置到环境变量JAVA_HOME即可。解压其实就相当于安装完成,查看版本。进入解压目录下的bin目录,运行java-version。运行结果如下:运行代码的常见方式就是我们平时使用的方式,使用GrralVM作为OpenJDK,但是将即时编译器换成了Graal。就是上面提到的第一种方法。安装完成后就可以当做普通的JDK来使用了,比如javac、jps、jmap等都可以直接使用。大部分人还是用IDEA,所以直接在IDEA里面用就行了。1、随意创建一个Java项目。2.创建完成后,打开项目设置。3、在打开的项目设置弹框中选择SDKs,点击加号,选择之前解压的GraalVM目录。4.然后选择刚刚添加的JDK。5.最后运行一段测试代码。publicclassHelloWorld{publicstaticvoidmain(String[]args)throwsException{System.out.println("HelloGraalVM!");Thread.sleep(1000*100*100);}}上面的运行方式,其实就相当于上面说的第一种运行方式,native-image方式。这种模式被AOT编译成机器码,以可执行文件的形式出现。native-image可以以命令行的形式执行,也可以结合Maven执行。我这里直接演示Maven的使用。毕竟我习惯了用IDEA配合Maven。1.安装原生图像工具包。native-image是AOT编译打包的工具。在继续以下步骤之前先安装它。安装GraalVM后,bin目录下有一个工具叫gu。使用此工具进行安装。如果将bin目录添加到环境中,只需运行以下命令即可安装它。如果guinstallnative-image没有将bin目录添加到环境变量中,则进入bin目录执行以下命令进行安装。./guinstallnative-image的过程可能会比较慢,因为要从github上下载东西。如果一次失败(比如超时),再试两次。2.配置Maven配置各种版本
