哈Ha! 鉴于不是有关Oracle有关Java许可政策的最新消息,从Oracle版本转移到
OpenJDK的问题变得越来越尖锐。 长期以来,OracleLabs的Odanko一直在做一个很酷的事情,叫做
GraalVM ,这是一个用Java编写的很酷的
JIT编译器,也是一个运行时,用于以JavaScript,Ruby,Python,C,C ++,Scala,Kotlin, R,克洛瑞尔。 令人印象深刻,对不对? 但我不想告诉您多语言环境的凉爽。 我们将讨论将最新的grail程序集集成到OpenJDK 11生态系统中的困难,以及一些有关性能的内容,相当多...
首先是这个词
我与
graalvm相识的故事始于2017年的
小丑 。
克里斯·西顿 (
Chris Seaton )在会上详细介绍了编译器的内部
原理,并展示了使用圣杯交付中的本机映像作为示例进行
AOT编译的妙处(这是一个将Java代码编译成本机二进制文件的笑话)。
这份报告之后,我接受了很长时间的培训,以编译我的宠物项目的本机二进制文件,拐杖和耙子以便在所有地方获得反映(可能还不行!),最后偶然发现了IO的未解决问题(有些东西那里没有和动物园管理员一起起飞,现在我不记得了。) 在本机图像上吐出:-(
2018年,奥列格·谢拉耶夫(Oleg Shelaev)关于AOT的超级详细报告中,同样的小丑和相同的graalvm。
在报告和演示中,一切看起来都很酷,而且光盘上还有一个宠物项目……该是时候揭露终端机,挥舞新的圣杯候选者并投入战斗了! 我们会碰一下。
世界你好
接触并踢一个小小的JIT(在撰写本文时是
ce-1.0.0-rc14版本),我们将使用一段代码来测试站点
https://graalvm.org的性能,这是我们的第一个示例。
没有任何构建系统的java项目(甚至是
Hello World )正在做什么? 没错,只有Javac会学习做饭的人。 我们不会学习烹饪Javak,让Maven引导Javak。
因此,满足pom.xml:
pom.xml<?xml version="1.0"?> <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>my-app</name> <url>http://maven.apache.org</url> <properties> <java.version>11</java.version> <graalvm.version>1.0.0-rc14</graalvm.version> </properties> <profiles> <profile> <id>jdk11</id> <activation> <jdk>11</jdk> </activation> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.10</version> <executions> <execution> <id>copy</id> <phase>process-test-classes</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.mycompany.app.App</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> </profile> </profiles> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>${java.version}</release> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.graalvm.compiler</groupId> <artifactId>compiler</artifactId> <version>${graalvm.version}</version> </dependency> <dependency> <groupId>org.graalvm.truffle</groupId> <artifactId>truffle-api</artifactId> <version>${graalvm.version}</version> </dependency> <dependency> <groupId>org.graalvm.sdk</groupId> <artifactId>graal-sdk</artifactId> <version>${graalvm.version}</version> </dependency> <dependency> <groupId>org.graalvm.js</groupId> <artifactId>js</artifactId> <version>${graalvm.version}</version> </dependency> <dependency> <groupId>org.graalvm.js</groupId> <artifactId>js-scriptengine</artifactId> <version>${graalvm.version}</version> </dependency> </dependencies> </project>
项目文件结构如下所示:

类代码
com.mycompany.app.App (来自graalvm.org的复制粘贴示例):
App.java package com.mycompany.app; public class App { static final int ITERATIONS = Math.max(Integer.getInteger("iterations", 1), 1); public static void main(String[] args) { String sentence = String.join(" ", args); for (int iter = 0; iter < ITERATIONS; iter++) { if (ITERATIONS != 1) System.out.println("-- iteration " + (iter + 1) + " --"); long total = 0, start = System.currentTimeMillis(), last = start; for (int i = 1; i < 10_000_000; i++) { total += sentence.chars().filter(Character::isUpperCase).count(); if (i % 1_000_000 == 0) { long now = System.currentTimeMillis(); System.out.printf("%d (%d ms)%n", i / 1_000_000, now - last); last = now; } } System.out.printf("total: %d (%d ms)%n", total, System.currentTimeMillis() - start); } } }
代码
模块info.java :
module-info.java module com.mycompany.app {}
嗯,是空的。但是它是空的,因为我想向您展示自定义Java(稍后再介绍自定义Java)将如何使用我们的应用程序需要的未声明模块发誓。
第一煎饼...
不块状。 让我们组装我们的项目并启动它。 这样说:
mvn clean package
我们推出:
$JAVA_HOME/bin/java -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler -Diterations=10 -jar target/my-app-1.0-SNAPSHOT.jar In 2017 I would like to run ALL languages in one VM.
有两个要点:
JVMCI是从版本9开始出现在Java中的实验性事物,因此我们需要:
启动结果-迭代1-1(1466毫秒)
2(461毫秒)
3(463毫秒)
4(138毫秒)
5(151毫秒)
6(159毫秒)
7(266毫秒)
8(128毫秒)
9(144毫秒)
总计:69999993(3481毫秒)
-迭代2-1(233毫秒)
2(169毫秒)
3(121毫秒)
4(205毫秒)
5(170毫秒)
6(152毫秒)
7(227毫秒)
8(158毫秒)
9(108毫秒)
总计:69999993(1644毫秒)
-迭代3-1(98毫秒)
2(102毫秒)
3(98毫秒)
4(102毫秒)
5(95毫秒)
6(96毫秒)
7(101毫秒)
8(95毫秒)
9(97毫秒)
总计:69999993(990毫秒)
-迭代4-1(109毫秒)
2(114毫秒)
3(97毫秒)
4(98毫秒)
5(100毫秒)
6(103毫秒)
7(125毫秒)
8(108毫秒)
9(100毫秒)
总计:69999993(1056毫秒)
-迭代5-1(98毫秒)
2(100毫秒)
3(105毫秒)
4(97毫秒)
5(95毫秒)
6(99毫秒)
7(95毫秒)
8(123毫秒)
9(98毫秒)
总计:69999993(1010毫秒)
-迭代6-1(99毫秒)
2(95毫秒)
3(102毫秒)
4(99毫秒)
5(96毫秒)
6(100毫秒)
7(99毫秒)
8(99毫秒)
9(104毫秒)
总计:69999993(993毫秒)
-迭代7-1(100毫秒)
2(104毫秒)
3(95毫秒)
4(96毫秒)
5(97毫秒)
6(95毫秒)
7(94毫秒)
8(108毫秒)
9(108毫秒)
总计:69999993(1000毫秒)
-迭代8-1(100毫秒)
2(106毫秒)
3(99毫秒)
4(95毫秒)
5(97毫秒)
6(97毫秒)
7(101毫秒)
8(99毫秒)
9(101毫秒)
总计:69999993(1012毫秒)
-迭代9-1(105 ms)
2(97毫秒)
3(98毫秒)
4(96毫秒)
5(99毫秒)
6(96毫秒)
7(94毫秒)
8(98毫秒)
9(105毫秒)
总计:69999993(993毫秒)
-迭代10-1(107 ms)
2(98毫秒)
3(99毫秒)
4(100毫秒)
5(97毫秒)
6(101毫秒)
7(98毫秒)
8(103毫秒)
9(105毫秒)
总计:69999993(1006毫秒)
我们在这里看到什么? 而且我们看到第一次迭代是最长的(3.5秒),此JIT正在预热。 然后,一切都会或多或少变得平滑(在一秒钟之内)。
但是,如果我们给Java一个新版本的圣杯呢? 快做决定:
java -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler -Diterations=10 --module-path=target/lib --upgrade-module-path=target/lib/compiler-1.0.0-rc14.jar -jar target/my-app-1.0-SNAPSHOT.jar In 2017 I would like to run ALL languages in one VM.
新的圣杯发射结果-迭代1-1(1789 ms)
2(547毫秒)
3(313毫秒)
4(87毫秒)
5(88毫秒)
6(87毫秒)
7(83毫秒)
8(92毫秒)
9(87毫秒)
总计:69999993(3259毫秒)
-迭代2-1(241毫秒)
2(161毫秒)
3(152毫秒)
4(195毫秒)
5(136毫秒)
6(129毫秒)
7(154毫秒)
8(176毫秒)
9(109毫秒)
总计:69999993(1553毫秒)
-迭代3-1(109毫秒)
2(103毫秒)
3(113毫秒)
4(172毫秒)
5(141毫秒)
6(148毫秒)
7(111毫秒)
8(102毫秒)
9(101毫秒)
总计:69999993(1211毫秒)
-迭代4-1(96毫秒)
2(96毫秒)
3(104毫秒)
4(98毫秒)
5(96毫秒)
6(97毫秒)
7(98毫秒)
8(96毫秒)
9(95毫秒)
总计:69999993(972毫秒)
-迭代5-1(97毫秒)
2(93毫秒)
3(99毫秒)
4(97毫秒)
5(97毫秒)
6(97毫秒)
7(95毫秒)
8(98毫秒)
9(94毫秒)
总计:69999993(965毫秒)
-迭代6-1(96毫秒)
2(95毫秒)
3(96毫秒)
4(99毫秒)
5(102毫秒)
6(94毫秒)
7(99毫秒)
8(115毫秒)
9(109毫秒)
总计:69999993(1001毫秒)
-迭代7-1(98毫秒)
2(96毫秒)
3(99毫秒)
4(98毫秒)
5(118毫秒)
6(98毫秒)
7(95毫秒)
8(99毫秒)
9(116毫秒)
总计:69999993(1017毫秒)
-迭代8-1(95 ms)
2(99毫秒)
3(99毫秒)
4(106毫秒)
5(101毫秒)
6(101毫秒)
7(93毫秒)
8(97毫秒)
9(108毫秒)
总计:69999993(993毫秒)
-迭代9-1(102 ms)
2(95毫秒)
3(97毫秒)
4(125毫秒)
5(94毫秒)
6(101毫秒)
7(100毫秒)
8(95毫秒)
9(96毫秒)
总计:69999993(1008毫秒)
-迭代10-1(97 ms)
2(97毫秒)
3(99毫秒)
4(112毫秒)
5(102毫秒)
6(96毫秒)
7(96毫秒)
8(98毫秒)
9(96毫秒)
总计:69999993(988毫秒)
如我们所见,结果并没有太大不同。
我忘了 好吧,如果没有新型的JIT编译器,我们不会尝试运行相同的东西。 我们将做:
java -Diterations=10 -jar target/my-app-1.0-SNAPSHOT.jar In 2017 I would like to run ALL languages in one VM.
没有新的JIT编译器的结果-迭代1-1(372毫秒)
2(271毫秒)
3(337毫秒)
4(391毫秒)
5(328毫秒)
6(273毫秒)
7(239毫秒)
8(271毫秒)
9(250毫秒)
总计:69999993(2978毫秒)
-迭代2-1(242毫秒)
2(253毫秒)
3(253毫秒)
4(240毫秒)
5(245毫秒)
6(275毫秒)
7(273毫秒)
8(263毫秒)
9(234毫秒)
总计:69999993(2533毫秒)
-迭代3-1(237 ms)
2(235毫秒)
3(234毫秒)
4(246毫秒)
5(242毫秒)
6(238毫秒)
7(244毫秒)
8(243毫秒)
9(253毫秒)
总计:69999993(2414毫秒)
-迭代4-1(244毫秒)
2(249毫秒)
3(245毫秒)
4(243毫秒)
5(232毫秒)
6(256毫秒)
7(321毫秒)
8(303毫秒)
9(249毫秒)
总计:69999993(2599毫秒)
-迭代5-1(246毫秒)
2(242毫秒)
3(248毫秒)
4(256毫秒)
5(280毫秒)
6(233毫秒)
7(235毫秒)
8(266毫秒)
9(246毫秒)
总计:69999993(2511毫秒)
-迭代6-1(292毫秒)
2(368毫秒)
3(339毫秒)
4(251毫秒)
5(267毫秒)
6(259毫秒)
7(289毫秒)
8(262毫秒)
9(357毫秒)
总计:69999993(3058毫秒)
-迭代7-1(284毫秒)
2(258毫秒)
3(248毫秒)
4(247毫秒)
5(266毫秒)
6(247毫秒)
7(242毫秒)
8(314毫秒)
9(265毫秒)
总计:69999993(2656毫秒)
-迭代8-1(239 ms)
2(238毫秒)
3(257毫秒)
4(282毫秒)
5(244毫秒)
6(261毫秒)
7(253毫秒)
8(295毫秒)
9(256毫秒)
总计:69999993(2575毫秒)
-迭代9-1(273 ms)
2(243毫秒)
3(239毫秒)
4(240毫秒)
5(250毫秒)
6(285毫秒)
7(266毫秒)
8(285毫秒)
9(264毫秒)
总计:69999993(2617毫秒)
-迭代10-1(245毫秒)
2(264毫秒)
3(258毫秒)
4(253毫秒)
5(239毫秒)
6(260毫秒)
7(251毫秒)
8(250毫秒)
9(256毫秒)
总计:69999993(2538毫秒)
结果是不同而体面的。
C2不对热门代码提供任何优化-每次迭代都在同一时间进行。
Graal能够优化热门代码,并且从长远来看可以提高性能。
那又怎样
当我们想为项目添加新功能,新工具,新虚拟机,新JIT时,这可能是您需要问自己和其他人(团队成员)的主要问题。
正如上面所写,我的故事始于2017年的Joker,然后人们进行了很多尝试以掌握AOT,现在我尝到了Java的JIT for Java的乐趣。
磁盘上的一个宠物项目是一种业务流程引擎,其中流程由应用程序开发人员在浏览器UI中绘制,并且它们具有用JS编写脚本的能力,该脚本将在JVM上运行。
他们承诺在Java的未来版本中删除nashorn,GraalVM逐渐接近该版本。
好吧,问题的答案是这样的:
- 我们希望运行时运行JS(不仅是)
- 要速度准时
- 我们希望启动pet-project应用程序像以前一样在8-ke上出现(没有任何
--module-path
和 --upgrade-module-path
但有一个新的圣杯组件)
捷联
上面问题的答案列表中的前两点很清楚,让我们处理后者。
事实上,developers-admins-devops很懒惰,不喜欢做额外的工作(我也很喜欢),他们尝试使所有内容自动化并将其打包到一个可以作为简单binar运行的现成捆绑中。 好了,有一个问题,让我们解决。
我们从Java 9+的世界中获得了一个相对较新的工具,它的名称为
jlink 。 我们正在尝试将应用程序与所有必需的库打包在一起:
jlink --module-path target/classes:target/lib:$JAVA_HOME/jmods --add-modules com.mycompany.app --launcher app=com.mycompany.app/com.mycompany.app.App --compress 2 --no-header-files --no-man-pages --strip-debug --output test
我们描述了多少种各种主要参数:
--module-path目标/类:目标/库:$ JAVA_HOME / jmods
--add-modules com.mycompany.app
--launcher app = com.mycompany.app / com.mycompany.app.App
-输出测试
您可以向Google的叔叔询问其他参数,所有这些参数都是为了减小捆绑包的总尺寸。
让我们看一下结果:

在test / bin / app内部,有一个简单的sh脚本可以在Java旁边的应用程序上启动我们的应用程序:
在C2上运行
test / bin / app :
./test/bin/app In 2017 I would like to run ALL languages in one VM.
结果-迭代1-1(315毫秒)
2(231毫秒)
3(214毫秒)
4(297毫秒)
5(257毫秒)
6(211毫秒)
7(217毫秒)
8(245毫秒)
9(222毫秒)
总计:69999993(2424毫秒)
-迭代2-1(215毫秒)
2(215毫秒)
3(223毫秒)
4(224毫秒)
5(217毫秒)
6(208毫秒)
7(208毫秒)
8(222毫秒)
9(222毫秒)
总计:69999993(2164毫秒)
-迭代3-1(206毫秒)
2(226毫秒)
3(234毫秒)
4(211毫秒)
5(212毫秒)
6(213毫秒)
7(210毫秒)
8(245毫秒)
9(223毫秒)
总计:69999993(2216毫秒)
-迭代4-1(222毫秒)
2(233毫秒)
3(220毫秒)
4(222毫秒)
5(221毫秒)
6(219毫秒)
7(222毫秒)
8(216毫秒)
9(220毫秒)
总计:69999993(2215毫秒)
-迭代5-1(231毫秒)
2(230毫秒)
3(221毫秒)
4(226毫秒)
5(227毫秒)
6(223毫秒)
7(215毫秒)
8(216毫秒)
9(219毫秒)
总计:69999993(2234毫秒)
-迭代6-1(227毫秒)
2(218毫秒)
3(221毫秒)
4(254毫秒)
5(222毫秒)
6(212毫秒)
7(214毫秒)
8(222毫秒)
9(222毫秒)
总计:69999993(2241毫秒)
-迭代7-1(217毫秒)
2(225毫秒)
3(222毫秒)
4(223毫秒)
5(227毫秒)
6(221毫秒)
7(219毫秒)
8(226毫秒)
9(219毫秒)
总计:69999993(2217毫秒)
-迭代8-1(218 ms)
2(242毫秒)
3(219毫秒)
4(218毫秒)
5(224毫秒)
6(226毫秒)
7(223毫秒)
8(220毫秒)
9(219毫秒)
总计:69999993(2228毫秒)
-迭代9-1(234 ms)
2(218毫秒)
3(217毫秒)
4(217毫秒)
5(225毫秒)
6(222毫秒)
7(216毫秒)
8(226毫秒)
9(214毫秒)
总计:69999993(2212毫秒)
-迭代10-1(226 ms)
2(230毫秒)
3(215毫秒)
4(238毫秒)
5(225毫秒)
6(218毫秒)
7(218毫秒)
8(215毫秒)
9(228毫秒)
总计:69999993(2233毫秒)
现在在graalvm上(通过定义在
JLINK_VM_OPTIONS变量中运行所需的标志):
结果:
Error occurred during initialization of boot layer java.lang.module.FindException: Module jdk.internal.vm.ci not found
好吧,我们已经航行了……现在,我们回想起我们在模块化环境中使用Java 11,我们将应用程序构建为模块,但是他们没有告诉任何人所使用的模块。 现在该修复它了。
新版本的module-info.java:
module com.mycompany.app { requires jdk.internal.vm.compiler; requires org.graalvm.sdk; requires org.graalvm.truffle; requires transitive org.graalvm.js; requires transitive org.graalvm.js.scriptengine; }
我们收集 ,删除测试目录,
链接 。
结果:
Error: automatic module cannot be used with jlink: icu4j from file:///home/slava/JavaProjects/graal-js-jdk11-maven-demo/target/lib/icu4j-62.1.jar
什么样的“自动模块kennote bi uzd”? 而且此jlink告诉我们icu4j
库不包含module-info.class。 此类需要出现在指定的lib中是什么:
- 了解任何模块使用的模块列表并创建module-info.java ,定义应该从外部可见的所有软件包
- 编译module-info.java为
- 将已编译的module-info.java与任何其他文件一起放入dzharnik
走吧
包含所有内容的module-info.java文件将为我们从openjdk-11生成
jdeps实用程序:

我们为icu4j编译module-info.java:

我们通过将
module-info.class推入dzharnik来更新它:
$JAVA_HOME/bin/jar uf target/lib/icu4j-62.1.jar -C target/modules module-info.class
链接 ,
运行 。
结果-迭代1-1(1216 ms)
2(223毫秒)
3(394毫秒)
4(138毫秒)
5(116毫秒)
6(102毫秒)
7(120毫秒)
8(106毫秒)
9(110毫秒)
总计:69999993(2619毫秒)
-迭代2-1(166毫秒)
2(133毫秒)
3(142毫秒)
4(157毫秒)
5(119毫秒)
6(134毫秒)
7(153毫秒)
8(95毫秒)
9(85毫秒)
总计:69999993(1269毫秒)
-迭代3-1(86毫秒)
2(81毫秒)
3(87毫秒)
4(83毫秒)
5(85毫秒)
6(100毫秒)
7(87毫秒)
8(83毫秒)
9(85毫秒)
总计:69999993(887毫秒)
-迭代4-1(84毫秒)
2(86毫秒)
3(88毫秒)
4(91毫秒)
5(85毫秒)
6(88毫秒)
7(87毫秒)
8(85毫秒)
9(85毫秒)
总计:69999993(864毫秒)
-迭代5-1(94毫秒)
2(86毫秒)
3(84毫秒)
4(83毫秒)
5(85毫秒)
6(86毫秒)
7(84毫秒)
8(84毫秒)
9(83毫秒)
总计:69999993(854毫秒)
-迭代6-1(83毫秒)
2(89毫秒)
3(87毫秒)
4(87毫秒)
5(86毫秒)
6(86毫秒)
7(91毫秒)
8(86毫秒)
9(85毫秒)
总计:69999993(865毫秒)
-迭代7-1(87毫秒)
2(86毫秒)
3(88毫秒)
4(90毫秒)
5(91毫秒)
6(87毫秒)
7(85毫秒)
8(85毫秒)
9(86毫秒)
总计:69999993(868毫秒)
-迭代8-1(84毫秒)
2(85毫秒)
3(86毫秒)
4(84毫秒)
5(84毫秒)
6(88毫秒)
7(85毫秒)
8(86毫秒)
9(86毫秒)
总计:69999993(852毫秒)
-迭代9-1(83 ms)
2(85毫秒)
3(84毫秒)
4(85毫秒)
5(89毫秒)
6(85毫秒)
7(88毫秒)
8(86毫秒)
9(83毫秒)
总计:69999993(850毫秒)
-迭代10-1(83毫秒)
2(84毫秒)
3(83毫秒)
4(82毫秒)
5(85毫秒)
6(83毫秒)
7(84毫秒)
8(94毫秒)
9(93毫秒)
总计:69999993(856毫秒)
万岁! 我们做到了! 现在,我们有了以Java运行的sh-script形式的被禁止的应用程序,其中包含所有必要的模块(包括新鲜的graalvm),并带有优先级和年轻女士。
聚苯乙烯
Java不会感到无聊,而是在每个发行版中为您提供新的思路。 尝试新功能,进行实验,分享经验。 我希望我能尽快写一篇文章,介绍如何禁止我的宠物项目的一部分(有vert.x,异步和js脚本-这将很有趣)。
但是……这是我关于哈布雷的第一篇文章,请不要用力。