快来了解JDK10中引入的全新JIT编译器:Graal
Posted ImportSource
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快来了解JDK10中引入的全新JIT编译器:Graal相关的知识,希望对你有一定的参考价值。
在()文中,我们提到jdk10中包含有一个实验性质的编译器(compiler)。它的名字叫做:Graal。这是一个基于Java的编译器(也就是使用Java语言来写的编译器)。
在Graal的github首页介绍中:
GraalVM is a project based in Oracle Labs developing a new JIT Compiler and Polyglot Runtime for the JVM.
你会发现他们对自己的定位是开发一个全新的JIT Compiler。
JDK9被用作AOT的编译器(静态)
然而在Jdk9 的时候,就引入了Graal。但那时候的graal被用来作为一个AOT编译器。也就是静态编译。
就是在启动虚拟机之前将Java类编译为本地代码(native code)。
具体在JEP 295中可以看到细节:
上面的图中也展示了引入AOT的动机。
动机
就是因为JIT虽然也比较快,但我们知道当下的JIT编译器需要花很长的时间才能达到阈值(无论是client模式还是server模式),从而才会触发JIT编译。事实上一些不经常被用到的java方法可能永远都不会被提前编译为本地代码(native code)。那这些没有机会被编译为native code的方法,事实上就是每次都需要解释执行,虽然性能也还好,但毕竟没有被提前编译为native code的代码快,所以性能上是相对差的。
再加上其他的一些编程语言引入了AOT的编译模式,jdk也不敢怠慢,于是就在jdk9的时候引入了基于Graal的AOT静态编译器。
事实上,我们也可以通过jdk10的源码中看到jaotc的目录:
JDK10又被用作JIT编译器(实验)
再回到本文开头那里,在JDK10的时候,Graal又被作为JIT编译器的一种选择,虽然是实验性的。
由于在9中已经引入了Graal,并且基于JVMCI接口做了适配。
JVMCI:是一个基于Java的JVM编译器接口。这个接口的目的,就是希望一些用java语言编写的编译器能够被用作JVM的动态编译器。比如:Graal编译器等。
所以JDK10就直接把已在jdk中的Graal用作JIT编译器了。但目前还只是作为实验和测试之用,并不具备商用的能力。
未来极有可能作为下一代 Java-based JIT动态编译器而被商用。
截止目前这个基于Graal的JIT编译器暂时只能用在Linux/x64平台。
并且在性能上达到甚至超越现有的JIT编译器并不是此Graal JIT编译器的目标。
那么Graal究竟是如何工作的呢?
JVMCI
上面我们已经介绍到一个接口JVMCI。从上面的介绍中我们知道他就是一个编译器接口。编译的这个动作,无非就是把一个方法编译成机器码(machine code),那么接口方法应该是这样:
interface JVMCICompiler {
byte[] compileMethod(byte[] bytecode);
}
就是把一个方法通过byte数组传入进去,然后返回一个byte[]数组而已。
但事实上编译的时候可能需要更多的内容。所以在jdk源码中的jvmci接口是下面这样的:
public interface JVMCICompiler {
int INVOCATION_ENTRY_BCI = -1;
/**
* Services a compilation request. This object should compile the method to machine code and
* install it in the code cache if the compilation is successful.
*/
CompilationRequestResult compileMethod(CompilationRequest request);
}
参数是一个CompilationRequest,然后返回的结果是一个CompilationRequestResult。我们再来看看这个两个接口(或类)。
/**
* Represents a request to compile a method.
*/
public class CompilationRequest {
private final ResolvedJavaMethod method;
private final int entryBCI;
/**
* Creates a request to compile a method starting at its entry point.
*
* @param method the method to be compiled
*/
public CompilationRequest(ResolvedJavaMethod method) {
this(method, -1);
}
/**
* Creates a request to compile a method starting at a given BCI.
*
* @param method the method to be compiled
* @param entryBCI the bytecode index (BCI) at which to start compiling where -1 denotes the
* method's entry point
*/
public CompilationRequest(ResolvedJavaMethod method, int entryBCI) {
assert method != null;
this.method = method;
this.entryBCI = entryBCI;
}
/**
* Gets the method to be compiled.
*/
public ResolvedJavaMethod getMethod() {
return method;
}
/**
* Gets the bytecode index (BCI) at which to start compiling where -1 denotes a non-OSR
* compilation request and all other values denote an on stack replacement (OSR) compilation
* request.
*/
public int getEntryBCI() {
return entryBCI;
}
@Override
public String toString() {
return method.format("%H.%n(%p)@" + entryBCI);
}
}
/**
* Provides information about the result of a {@link CompilationRequest}.
*/
public interface CompilationRequestResult {
/**
* Determines if the compilation was successful.
*
* @return a non-null object whose {@link Object#toString()} describes the failure or null if
* compilation was successful
*/
Object getFailure();
}
我们再来看看HotSpot里具体的基于JVMCI接口的Graal的实现。
看了这么多。我想说的意思是你完全可以在compileMethod加上一些自己的逻辑,因为这个方法就是编译器把bytecode编译为machine code的地方。
class HotSpotGraalCompiler implements JVMCICompiler {
CompilationRequestResult compileMethod(CompilationRequest request) {
System.err.println("现在去编译方法:" + request.getMethod().getName());
...
}
}
好,你大概知道JVMCI是个什么吧。
Graal Graph
没错,看到graph这个单词就应该大体要说什么了。通过前面的介绍你已经知道了Graal就是负责把输入的byte[]转换成另一个byte[]。现在我们就来说Graal在转换的过程中的一点点理论和数据结构的东西。因为有一点别致。
本质上来说,编译器就是处理和操作你的代码。在内部它肯定要把你的代码转换成某种数据结构来表示程序。那么bytecode或指令列表算是一种方式。但是这些都不够形象。
Graal就想到了使用图来表示待编译的代码。比如说你现在有一个简单加法操作,就是把两个变量相加。那么这个图(graph)就是有两个节点负责分别load本地变量,还有第三个节点是加法操作,两条边就表示加载局部变量的结果,然后指向加号节点。用语言描述有点绕,直接看图:
也有人把这种表示叫做程序依赖图(program-dependence-graph)。
从jdk10的代码中也可以看到有关graph的数据结构。
更多细节代码请去看JDK10的源码
总结
总之,Graal作为一个编译器。它可以被用来干任何它适合干的事情。在JDK9中引入它用作AOT静态编译器,在JDK10的时候使用它来作为一个全新的JIT编译器(实验的)。通过介绍Graal,让我们知道了在JVM中引入基于Java语言的编译器会带来现在没有的好处。为了支持JIT编译未来面向开发者开放,JDK9中引入了JVMCI接口的概念。过去你修改了JVM的代码,你不得不重新发布整个代码库。如今你可以把JVM和编译器分开部署,你可以部署一个A版本的JVM,然后再单独部署一个B版本的编译器,这都是JVMCI为我们带来的好处。
以上是关于快来了解JDK10中引入的全新JIT编译器:Graal的主要内容,如果未能解决你的问题,请参考以下文章