Master-slave
发布于 2019-10-29 / 27 阅读
1
0

使用GraalVM将Java代码编译成可执行文件

前言

GraalVM是基于HotSpotVM开发的一款新的虚拟机,可以实现多语言混合编程的目的,支持R、Ruby、Python、JS、Node等多种语言。通过这个特性我们可以便捷地使用在某些框架中已经存在的库的目的,减少重复造轮子的时间。

同时我们还可以通过Oracle开发的Truffle框架实现我们自己的语言,并且可以集成到GraalVM中,从而可以很方便地获得监控、调试代码等特性。

其中GraalVM还包含了一项有趣的功能是可以根据不同的平台将代码编译成对应平台下的本地可执行文件,如Windows下的exe可执行文件。

PS:现阶段的Windows支持是实验性的

创建Native-image

测试代码

public class PrintInExe
{
    public static void main(String[] args) {
        System.out.println("没想到吧,哈哈!" + args[0]);
    }
}

Linux(测试使用Centos 6.10)

#1 下载GraalVM
$ wget https://github.com/oracle/graal/releases/download/vm-19.2.1/graalvm-ce-linux-amd64-19.2.1.tar.gz
#2 解压
$ tar -xzvf graalvm-ce-linux-amd64-19.2.1.tar.gz
#3 将java文件放在同一目录下并编译java代码
$ graalvm-ce-19.2.1/bin/javac PrintInExe.java
#4 安装native-image工具
$ graalvm-ce-19.2.1/bin/gu install native-image
#5 生成可执行文件
$ graalvm-ce-19.2.1/bin/native-image PrintInExe
#6 测试
$ ./printinexe 就是这么简单
  # 没想到吧,哈哈!就是这么简单
​

Windows

配置GraalVM环境

GitHub GraalVM下载最新版本的GraalVM压缩包并解压,当前的最新版是19.2.1。社区版的GraalVM使用OpenJDK 8作为默认的JDK,但在后续会支持JDK 11(LTS)。

解压后可以看到目录结构和标准的JDK是比较类似的,GraalVM主要组件存在于jre目录下。在bin目录中我们可以找到"native-image.cmd",点开可以发现其最后的指向是位于"jre/lib/svm/bin"目录下的同名批处理文件。

编译java代码并生成映像

通过bin目录下的javac命令将上述java代码编译成class文件,并且因为windows版本已经安装了native-imagee,所以我们可以使用native-image命令直接进行编译......

如果出现了和Linux同样的结果并看到这样的结果,恭喜你完成了编译。

[printinexe:19596] classlist: 4,820.60 ms 
[printinexe:19596] (cap): 8,007.42 ms 
[printinexe:19596] setup: 10,590.92 ms 
[printinexe:19596] (typeflow): 12,300.40 ms ... 
[printinexe:19596] [total]: 68,108.40 ms

但大多数非“Windows爱好者”估计碰到的更有可能是

[printinexe:20772] classlist: 4,273.64 ms 
[printinexe:20772] (cap): 696.86 ms 
[printinexe:20772] setup: 1,209.42 ms 
Error: Unable to compile C-ABI query code. Make sure GCC toolchain is installed on your system. 
Error: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception 
Error: Image build request failed with exit status 1

解决GCC toolchain问题

  1. 微软官网下载镜像文件,选择GRMSDKX_EN_DVD.iso下载。

  2. 检查是否已经安装 Visual C++ 2010, 如果已经安装,在安装前需要先卸载因为在安装SDK过程中会重新安装VC2010,并且如果已经存在则会导致安装失败。

  3. 运行setup.exe,如果安装过程提示关于.Net Framework 4的版本错误,参见参考资料第四条通过修改注册表版本信息解决。

  4. 不出意外地话安装顺利结束后,在开始菜单的Microsoft Windows SDK v7.1目录下找到Windows SDK 7.1 Command Prompt,使用该命令窗口取代cmd命令行,再次运行上面的native-image命令则可获得正确的结果。

编译结果

命令执行完成后可以看到对应的目录下多了4个文件,其中的printinexe则是我们需要的可执行文件。

printinexe.exe

printinexe.pdb

printinexe.exp

printinexe.lib

PrintInExe.class

PrintInExe.java

在写的同时又发现了一个神奇的中文显示问题,编写java代码的时候使用的是UTF8编码,在cmd窗口下编译完成后再在使用默认的“ANSI”编码的Windows SDK窗口运行上面的代码时返回了乱码。

printinexe 就是这么复杂 
 #娌℃兂鍒板惂锛屽搱鍝堬紒就是这么复杂

原因是在编译java代码是没有使用-encoding utf-8参数指明编译时使用的编码,改正过来后再次编译,生成映像再运行测试即可

# .../javac -encoding utf-8 PrintInExe.java
# .../native-image PrintInExe
printinexe 就是这么复杂
# 没想到吧,哈哈!就是这么复杂

后记

使用GraalVM可以很方便地帮助我们在不同平台生成对应的可执行文件,而且上面的例子中生成的exe文件是脱离了JVM独立运行的。这让我们在即使不熟悉C的情况下也可以生成很方便地通过Java代码生成一些工具命令。而且由于是本地地镜像,在启动时间上与通过JVM运行速度提升十分明显。 简单地几行代码生成的exe大小在8M左右,虽然和代码量对比起来很大,但在Java运行环境中一个jre就需要几百M的空间了,对比起来空间的占用小了很多,更适合用于产品环境。 本地镜像的生成同时支持GraalVM多语言的特性,可以使用混合语言进行编程,例如在Java代码中嵌套JS代码。

当然native-image为了实现上的简洁有一些Java中支持的功能其仍未完全支持,可以在Limitation in GraalVM中进一步了解。

参考资料

  1. 历数 GraalVM 的十大用途

  2. Ten things in GraalVM

  3. 通过GraalVM创建本地可执行文件

  4. .NET Framework 4 版本不匹配导致的安装失败问题


评论