十分钟带你了解 Oracle 最新的 JVM 技術——GraalVM

十分钟带你了解 Oracle 最新的 JVM 技術——GraalVM

GraalVM 是 Oracle 发布的下世代 Java 虚拟机,2019.05 才发布了第一个 release 版本,分别有社区版和企业版

GraalVM 三大特点

1. 高效能运行 Java

使用 GraalVM 执行 Java 程式可以变得更快

2. 多语言并行

可以在 Java 里面同时使用多种语言,像是 JavaScript、R...

3. 快速启动

直接把 Java 应用编译成机器码,执行起来体积更小、启动速度更快

1. 高效能运行 Java

GraalVM 之所以能够更高效能运行 Java 应用,是因为使用到了 Graal 编译器技术,而 Graal 编译器是一个 JIT 编译器,但什么又是 JIT 编译器呢?

实际上,在 Java 里的编译器可以分为前端编译器和运行期编译器,前端编译器是指将 .java 编译成 .class 的过程,而运行期编译器则是指将 .class 字节码转变成机器码的过程,而运行期编译器的英文为 Just In Time Compiler,所以又能简称为 JIT 编译器

当初 JVM 开发团队把大部分的代码优化都放在运行期的编译器 JIT 上,而前端的编译器 javac 几乎没有任何代码优化措施,原因是因为这样可以让那些不是由 javac 产生的 .class 文件,也能享受到编译器优化所带来的好处,而前端编译器 javac 则专门负责处理 Java 的语法糖,将他转换为正常的字节码,因此可以说前端编译器 javac 是负责增进程序员开发效率,而运行其编译器 JIT 则是负责增进代码运行速度

  • 前端编译器 Javac : 负责将java中的语法糖,转换为正常的字节码
  • 运行器编译器 JIT : 负责代码优化


了解了 JIT 编译器之后,我们说回到 Graal 编译器这里

Graal 编译器是使用 Java 写的 JIT 编译器,虽然难免会让人联想到性能会比不上 HotSpot 使用 C++ 写的 C2 编译器,但是在各种实验之后,得到的数据显示对于 Java 应用而言,Graal 编译器和 C2 编译器的能力几乎不相上下(在已经预热完毕的前提下)

而对于 Scala 应用来说,Graal 编译器更是能达到 10% 以上的优化,这也是为什么 Twitter 大规模的使用 GraalVM 替换掉原本的 HotspotVM



不过在启动时,GraalVM 会比 HotspotVM 还慢,原因是必须先将 Graal 编译成机器码,这个是无可避免的。只是当预热完毕时,Graal 和 C2 的性能会不相上下,并且根据 Graal 的版本不断更新,这个数据只可能会更好

2. 多语言并行

GraalVM 最一开始被发明出来,就是为了让 Java 可以在一个运行期内,同时使用多种语言,从官网称呼 GraalVM 为 High-performance polyglot VM,也可以发现 Oracle 对 GraalVM 定位是一个多语言 jvm

为了实现多语言并行,GraalVM 引入了一层 Truffle framework,只要实现了该语言的直译器,就可以在 Java 应用中使用该语言

目前官方支援的语言仅有 Python、R、JavaScript,后续会陆续增加



具体实例

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

        Context context = Context.newBuilder().allowAllAccess(true).build();

        context.eval("js", "print('Hello World from JavaScript!');");
        context.eval("python", "print('Hello World from Python!')");
        context.eval("ruby", "puts 'Hello World from Ruby!'");
    }
}
Hello World from Java!
Hello World from JavaScript!
Hello World from Python!
Hello World from Ruby!

3. 快速启动

GraalVM 还有最后一项技术,就是 native image 快速启动,这项技术是在编译期时,就将 Java 应用直接编译成二进制的机器码,让这个程式可以像一般二进制的档案被运行



native image 带来的好处是可以更快速的启动一个 java 应用,以往如果要启动 java 程式,需要先启动 jvm 再载入 java 代码,然后再即时的将 .class 字节码编译成机器码,交给机器执行,非常耗时间和耗内存,而如果使用 native image,可以取得一个更小更快速的镜像,适合用在云部署上

native image 之所以可以快速启动,是因为他底层使用了 Ahead-of-time compile(提前编译),也就是说,他在编译期时,会把所有相关的东西,包含一个基底的 VM,一起编译成机器码,这个基底 VM 是 GraalVM 内部才有的东西,他只包含最基本的线程排成机制、垃圾回收,尽可能的缩小必要的 jvm 体积



虽然 native image 听起来很厉害,但是他也有不可抹灭的缺点,就是使用 native image 的程式,吞吐量会下降,原因是因为 java 程式很大一部分的优化都在 JIT 编译器中,而 native image 是没有使用到任何 JIT 提供的好处的

还有另一个缺点是,native image 并没有办法动态的加载类(因为所有东西必须要在编译期就决定好),所以也没办法使用反射等相关机制

不过对于这个问题,GraalVM 也有提出相对应的解法,就是在编译时,把所有可能的类全部编译进来,所以反射机制还是可以支持的,不然的话,整个 Spring framework 就不能使用 native image 了

目前 Spring 5 也打算开始支持 GraalVM native-image 的开箱即用设定,可以想像无服务器计算的 java 应用可能是之后的趋势,毕竟要有 native image 的快速启动特性,才能够达到无服务器计算的初衷



补充一下,无服务器计算就是指 Fuction as a Service,他的目的是希望应用不用一直运行着,只有当有请求来的时候,才快速启动这个应用,然后请求一走就停掉这个应用
换句话说,不让应用在背景程式持续的启动着,而是有需要的时候才开启他
编辑于 09-21

文章被以下专栏收录