

初识Graal
source link: http://afoo.me/posts/2018-08-15-know_graal_today.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Java程序员应该很羡慕Go语言程序员能够编译出一个one-in-all的二进制可执行文件,然后本地直接执行, 而自己却要-cp指定一堆jar和路径,虽然可以打包成一个onejar,但依然要本地安装java运行环境,然后 java -jar x.jar
, 加上如果想赶时髦, 使用docker进行发布和部署, 还得忍受基础镜像几百兆的size, 实在不能称得上 多快好省
而且轻…
GraalVM虽然还没有发布正式版,但已经发布了几版RC,从它的发展历程中,让很多Java程序员看到了一条康庄大道, 多快好省
不再是梦。
GraalVM提供的native image工具可以帮助我们将java程序编译成一个本地可执行的二进制文件,这个特性可以让我们有很大的想象空间:
- 用java写工具, portable不是梦;
- 用docker封装发布, ADD一个几十兆的二进制可执行文件就行了;
- 微服务?更轻量, 启动更快,运行期内存更小…
光说不练假把式, 让我们先从简单的一个工具入手看看native image的魅力吧~
1 native image快速上手
GraalVM基于Java8,你可以理解成另一个SDK, 所以首先要跟JDK似的,下载安装并配置PATH, http://www.graalvm.org/downloads/ , 怎么搞我就不细说了, 暂时只有Mac和Linux版本可用。
我们创建一个Java项目,还是典型的HelloWorld:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.7</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.example.demo.DemoApplication</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
HelloWorld嘛, 你懂得:
public class DemoApplication { public static void main(String[] args) { System.out.println("Hello World"); } }
执行 mvn package
之后我们就获得一个可执行的jar包(因为我们用了shade插件),我们针对这个可执行jar包执行native image命令:
➜ one_jar_native_image_starter git:(master) ✗ $HOME/bin/graalvm-ce-1.0.0-rc5/Contents/Home/bin/native-image -jar target/demo-0.0.1-SNAPSHOT.jar Build on Server(pid: 99502, port: 64944) classlist: 616.43 ms (cap): 5,338.88 ms setup: 6,753.34 ms (typeflow): 6,509.05 ms (objects): 2,937.12 ms (features): 62.18 ms analysis: 9,645.27 ms universe: 400.46 ms (parse): 2,275.13 ms (inline): 1,730.84 ms (compile): 8,492.76 ms compile: 13,170.85 ms image: 1,541.81 ms write: 1,703.71 ms [total]: 33,919.21 ms
查看当前项目目录:
➜ one_jar_native_image_starter git:(master) ✗ ll total 11432 drwxr-xr-x@ 4 afuluo staff 128 Apr 29 08:31 src -rw-r--r--@ 1 afuluo staff 2282 Apr 29 08:38 pom.xml -rw-r--r-- 1 afuluo staff 931 Apr 29 08:38 demo.iml drwxr-xr-x 8 afuluo staff 256 Aug 15 16:25 target -rw-r--r-- 1 afuluo staff 1507 Aug 15 16:25 dependency-reduced-pom.xml -rwxr-xr-x 1 afuluo staff 5840784 Aug 15 16:26 demo-0.0.1-SNAPSHOT
可以看大, demo-0.0.1-SNAPSHOT就是生成的可执行二进制文件,可以直接执行: ./demo-0.0.1-SNAPSHOT
,不过默认生成的名字实在丑陋,所以,我们可以通过 -H:Name=<自定义名称>
命令行选项自己指定:
➜ one_jar_native_image_starter git:(master) ✗ $HOME/bin/graalvm-ce-1.0.0-rc5/Contents/Home/bin/native-image -jar target/demo-0.0.1-SNAPSHOT.jar -H:Name=hello Build on Server(pid: 99502, port: 64944) classlist: 464.16 ms (cap): 3,179.86 ms setup: 3,587.10 ms (typeflow): 4,150.58 ms (objects): 1,641.23 ms (features): 40.68 ms analysis: 5,951.36 ms universe: 330.50 ms (parse): 1,210.82 ms (inline): 2,054.93 ms (compile): 7,206.31 ms compile: 11,043.39 ms image: 968.90 ms write: 1,257.79 ms [total]: 23,640.19 ms
这样我们获得的二进制可执行文件名称就是hello:
➜ one_jar_native_image_starter git:(master) ✗ ll total 22840 drwxr-xr-x@ 4 afuluo staff 128 Apr 29 08:31 src -rw-r--r--@ 1 afuluo staff 2282 Apr 29 08:38 pom.xml -rw-r--r-- 1 afuluo staff 931 Apr 29 08:38 demo.iml drwxr-xr-x 8 afuluo staff 256 Aug 15 16:25 target -rw-r--r-- 1 afuluo staff 1507 Aug 15 16:25 dependency-reduced-pom.xml -rwxr-xr-x 1 afuluo staff 5840784 Aug 15 16:26 demo-0.0.1-SNAPSHOT -rwxr-xr-x 1 afuluo staff 5840856 Aug 15 16:27 hello
直接执行: ./hello
, Aha~
NOTE Mac版的native image编译完获得的二进制文件不能在linux上运行, 因为二进制文件都是编译后的本地machine code, 所以,要在哪个目标平台执行,需要用哪个平台的GraalVM编译哦~
2 深入native image
写写小工具, 前面的快速上手的知识基本就够了,但是要想构建稳健运行在生产环境的服务或者系统, 我们就得对Graal或者说native image有更深入的了解了。
为了简化,我们使用 KeeWeb(https://www.keevol.com) 框架构建一个简单的Web API,只提供一个GET的Endpoint,返回“HelloWorld”作为结果:
package com.keevol.demo.hello.webapinative import com.keevol.keeweb.{KeeWeb, Kontroller} import io.vertx.core.Vertx import io.vertx.core.http.HttpMethod import io.vertx.ext.web.RoutingContext class HelloController extends Kontroller{ override val path: String = "/" override val method: HttpMethod = HttpMethod.GET override def apply(rc: RoutingContext): Unit = { rc.response().end("Hello, there") } } object Hello{ val helloController = new HelloController def main(args: Array[String]): Unit = { val vertx = Vertx.vertx() KeeWeb.start(vertx, Seq(helloController)) System.in.read() // block server vertx.close() } }
生成onejar之后,用native-image编译成本地可执行文件:
$HOME/bin/graalvm-ce-1.0.0-rc5/Contents/Home/bin/native-image -H:+ReportUnsupportedElementsAtRuntime -H:Name=hello-server -jar build/libs/hello-webapi-native-1.0.0-SNAPSHOT-all.jar
然后执行: ``, 现在,我们就可以访问我们的web api了:
➜ ~ http http://localhost:1979 HTTP/1.1 200 OK Content-Length: 12 Hello, there
好吧,我其实把过程简化了,请注意 -H:+ReportUnsupportedElementsAtRuntime
这个参数,其实第一次编译,我们是编译不过的,通过这个参数,编译的结果就可以呈现给我们一些错误提示, 比如:
Caused by: java.lang.ClassNotFoundException: com.jcraft.jzlib.Inflater at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ...
实际上, Graal native-image在有些方面是有功能限制的,比如java reflection, native-image干的事情是一种AOT(Ahead Of Time Compiling),要AOT,就需要提前知道程序依赖的Class有哪些, 所以,一些运行期(Runtime)加载的类在AOT的时候是无法知道的, 但我们可以通过一些手段预先知会native-image有哪些类需要用到:
-H:ReflectionConfigurationFiles=reflection.json
我们选择第一种方式, 所以, 最终的native-image命令如下所示:
$HOME/bin/graalvm-ce-1.0.0-rc5/Contents/Home/bin/native-image --no-server -Djava.net.preferIPv4Stack=true -Dvertx.disableDnsResolver=true -jar build/libs/hello-webapi-native-1.0.0-SNAPSHOT-all.jar -H:Name=helloserver -H:+ReportUnsupportedElementsAtRuntime -H:ReflectionConfigurationFiles=reflection.json
当然, 很不幸的是, 我用的graalvm-1.0.0-rc5版本还是有bug和不支持的特性,导致执行 ./helloserver
的时候会抛出segment fault,或许等graal 1.0.0正式发布之后, 对各类库和框架的支持会更稳定些吧~
➜ hello-webapi-native git:(master) ✗ ./helloserver 19:03:47.562 [main] INFO com.keevol.keeweb.KeeHttpServer - start http server... [1] 7111 segmentation fault ./helloserver
3 限制与局限性
当然,除了反射之类的限制,将Java程序编译成native image还其他的限制,像JMX这种平台相关的特性就不要想了, 需要借助GraalVM Enterprise版提供的一些Profiler或者Debugger等商业特性, 或者自己加一些外围的监控支持,因为我们拿不到Truffle框架EST中添加的一些附加信息。
更多限制可以参考官方的文档: https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md
4 参考文档
5 小结
GraalVM无论是从性能上,还是理念上,都对Java生态产生了很大的促进作用, 虽然它还只是基于Java8, 也没有发布正式版,但我相信即使Java11发布了, GraalVM也将是Java基础设施中很重要的一员, 跟 福强科技(https://www.keevol.com) 同为Scala重度用户的 Twitter , 已经在生产环境使用GraalVM很长时间了, 这也充分证明了GrralVM的核心价值所在。
对于Java社区或者重度Java技术体系的公司来说, GraalVM将是值得投入的一个技术域, 使用得当,可以为公司节省大量的研发和基础设施费用。
Recommend
-
128
Understanding How Graal Works - a Java JIT Compiler Written in Java Chris Seaton, 3 November 2017 These are the notes for a talk I gave at Joker...
-
62
Java has quite possibly one of the worst ways to interact with native methods using JNI (Java Native Interface). JNI APIs follow weird conventions and the language boundary interop is unbelievably cumbersome to deal with....
-
51
Key Takeaways Java's C2 JIT compiler is end-of-life The new JVMCI compiler interface allows new compilers to be plugged in Oracle have developed Graal, a JIT writt...
-
32
Graal is an open source tool to conduct customizable, scalable and incremental analysis of source code I’ve been working on as a side project to the work I am doing inBitergia with
-
59
关键要点 Java的C2 JIT编译器寿终正寝。 新的JVMCI编译器接口支持可插拔编译器。 甲骨文开发了Graal,一个用Java编写的JIT,作为潜在的编译器替代方案。 Graal...
-
69
Nicolas Fränkel's blog
-
52
As a follow up after the vacations on the post Vert.x native image awesomeness!,I got some Twitter challenge about what about serverles?
-
21
Graal Autovectorization Here's our first prototype for Autovectorization in Graal, as mentioned in our email to the graal-dev ma...
-
28
The last two blog posts were focused on how to write a custom Kubernetes controller in Java. As usual, I’m writing a demo along with the posts: that allows me to face real issues, and be able to detail them. In this context, I had to handle a co...
-
17
Spring GraalVM Native 0.6.0 released The Spring team have just released version 0.6.0 of the
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK