前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >快来了解JDK10中引入的全新JIT编译器:Graal

快来了解JDK10中引入的全新JIT编译器:Graal

作者头像
ImportSource
发布2018-04-03 11:41:53
1.7K0
发布2018-04-03 11:41:53
举报
文章被收录于专栏:ImportSourceImportSource

在(JDK10要来了:下一代 Java 有哪些新特性?)文中,我们提到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),那么接口方法应该是这样:

代码语言:javascript
复制
interface JVMCICompiler {
    byte[] compileMethod(byte[] bytecode);
}

就是把一个方法通过byte数组传入进去,然后返回一个byte[]数组而已。

但事实上编译的时候可能需要更多的内容。所以在jdk源码中的jvmci接口是下面这样的:

代码语言:javascript
复制
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。我们再来看看这个两个接口(或类)。

代码语言:javascript
复制
/**
 * 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);
    }
}

代码语言:javascript
复制
/**
 * 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的地方。

代码语言:javascript
复制
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为我们带来的好处。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ImportSource 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档