上篇文件我们介绍了GraalVM强大的静态编译功能,能够让Java应用程序摆脱虚拟机的束缚,像其它本地编译的应用一样直接运行。那么GraalVM的神奇之处仅限于此吗?今天我们再来看看它的另一个重要特性—多语言混合开发
多语言平台
Java并不是唯一运行在JVM上的语言,这个我们都应该比较清楚了,比如Kotlin,Scala,Groovy这些其实都是运行在JVM之上的。这样语言不管上层语法千差万别,但最终都会被编译器(javac,kotlinc等)编译成符合JVM规范的字节码,所以它们能跑在JVM上,并且可以相互调用其实并不让人意外。
理论上任何上层语言都能够通过一定的转换,变成符合标准JVM规范的字节码,从而运行在JVM平台之上,但编写这样的编译器的成本非常高,而且受限于JVM字节码的结构何规范,有些语言特性可能也没办法很好的支持。因此GraalVM实现多语言平台采用了另一种方式,绕开了JVM字节码的限制,我们先来看一下官方给的架构图:
在这张图上,不但GraalVM不但能跑Kotlin,Scala这类传统的JVM语言,还有JS, Python, Ruby这样的脚本语言,甚至还有C和C++ 。初看起来确实很让人惊讶,不过先别急,我们慢慢来分析一下其中的原理。这张图底层的部分,Graal编译器,JVMCI,Hotspot JVM上一篇文章都讲过了,不理解的同学可以再翻一下之前的文章。我们重点来看一下Truffle这个新出现的框架。
这个东西显然是实现多语言平台的关键,官方对它的定义有点拗口,简单的说它是一个用于开发某种语言(比如JavaScript)的解释器的一套工具和框架,让不同的语言能够方便的实现自己的解释器,从而将各语言的源代码都能够解析成一种统一的数据格式,而这种数据格式是一种self-modifying Abstract Syntax Trees(AST,抽象语法树)。为了便于理解,你可以简单的认为它就是其它语言编译后的字节码(都是某种中间格式),Truffle框架还提供运行层面的支持,可以直接解释执行这样的AST,从而实现多语言运行在JVM之上。因此要将一门现有语言搬到GraalVM上来运行,你只需要使用Truffle框架为该门语言编写一个Interpreter(解释器),是不是看起来很简单?GraalVM官方目前也已经实现了包括JavaScript,Python等在内的多个Truffle解释器(GraalJS,GraalPython等),可以直接供开发者使用。
如果仅仅是这样,那么大家可能觉得这仅仅只是一个小玩具而已,毕竟通过解释器来执行脚本语言性能肯定很拉跨,完全无法和原生平台的执行效率相提并论。但不要小看GraalVM的雄心壮志,我们都知道JVM经过这么多年的发展,在编译优化,内存管理(垃圾回收)这些方面都有着非常强悍的积累(go的垃圾回收器的性能表现就长期不如Java),这才是运行在JVM之上的语言的真正的优势所在。但怎么让这些解释执行的脚本语言也享受JVM的优化呢?答案是JIT,JIT的概念这里就不多介绍了,我们再来看一张图,以Javascript和Java为例,看看它们在GraalVM上的执行细节
运行细节
先来看Java的执行路径,首先由javac编译成字节码,然后JVM的解释器一边解释执行,一边收集运行时信息,当部分代码达到阙值的时候(调用次数等统计指标)就会触发JIT编译,JVM将此部分代码通过Graal编译器编译成本地机器码,然后替换自身缓存中的此部分字节码,从而提升该部分代码的执行效率。JVM和JIT编译器之间通过JVMCI规范的接口进行交互,所以这里的Graal编译器也可以替换成传统的C1或者C2编译器。Kotlin,Scala等语言的执行方式也基本是这样。
再看一下Javascript,源码首先会被基于Truffle实现的JS解释器转换成内存中的AST格式,然后就可以被Truffle内部的解释器解释执行了,而Truffle也会像JVM一样,统计收集运行时信息,并将热点代码发送给Graal编译器去编译成本地机器码,最终将编译好的机器码替换AST中的相同部分,所以现在明白为什么叫做self-modifying Abstract Syntax Trees吧,这个AST树是能够自修改的。所以基于Truffle框架执行的三方脚本语言,也能享受到JIT编译器的各种优化,性能比纯解释执行要高出很多。而且Truffle是一个完全用Java编写的框架,所以它可以直接运行于底层的JVM之上。
上面的图还有一个Graal IR的东西,这个是个什么东西呢?这个主要是为了让Graal编译器能够实现语言无关的特性而使用的一种中间表示形式,不管是JAVA的字节码还是Javascript的AST,在Graal编译器内部都会被表示为Graal IR,然后对这种格式进行编译优化,最终形成机器码。
通过精心的设计和优化,在GraalVM上执行三方语言,性能已经非常接近原生平台了,部分场景下甚至会超过原生的平台。下图就是GraalVM官方对于Javascript执行性能与V8(公认最强的JS执行引擎)的对比: