网站首页 > java教程 正文
Java 9 引入了 aot 编译方式,能够将 class 文件直接编译成可执行二进制文件。目前 Java 9 的 early access 版本已经提供了编译工具,让我们来看看它的功能吧。
注意:按照 JEP 295 描述,目前版本的 AOT,仅支持 64 位 Linux 操作系统。
jaotc 使用
首先需要下载最新的Java 9(JDK),本文编写时,最新版本是Build 152。下载好的JDK 只需要解压即可使用,特别注意使用前设置好 PATH和JAVA_HOME两个环境变量,避免和机器上已经安装的 JDK 混淆。笔者安装到了 $HOME/bin/jdk-9,并设置了:
export PATH=~/bin/jdk-9/bin:$PATH
export JAVA_HOME=~/bin/jdk-9
需要使用jaotc,首先需要有个测试类,首先从 Hello World 开始:
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
代码非常简单,但是在执行 jaotc 之前,还需要将其编译成 class 文件,直接使用 javac 即可:
$ javac HelloWorld.java
执行成功之后,会生成 HelloWorld.class 文件。此时直接使用 java 命令,已经可以正常运行这个类:
$ java HelloWorld
Hello World!
这时,就可以基于这个 class 文件,通过jaotc命令将其编译成二进制文件了。
$ jaotc --output libHelloWorld.so HelloWorld.class
如果一切正常,会生成 libHelloWorld.so 文件。
如果出现类似Exception in thread "main" java.lang.UnsatisfiedLinkError: /home/babydragon/bin/jdk-9/lib/libjelfshim.so: libelf.so.1: 无法打开共享对象文件: 没有那个文件或目录的错误,是因为jaotc需要依赖 libelf 动态链接库来创建 elf 文件(最终生成的 libHelloWorld.so 文件是一个静态链接的 elf 文件)。笔者使用的是 Gentoo 系统,需要安装 dev-libs/elfutils 包,以提供 libelf.so 这个动态连接库。安装之后可以通过ldd命令进行确认:
$ ldd $JAVA_HOME/lib/libjelfshim.so
linux-vdso.so.1 (0x00007ffd001f3000)
libelf.so.1 => /usr/lib64/libelf.so.1 (0x00007f25ea2ce000)
libc.so.6 => /lib64/libc.so.6 (0x00007f25e9f35000)
libz.so.1 => /lib64/libz.so.1 (0x00007f25e9d1d000)
/lib64/ld-linux-x86-64.so.2 (0x0000562318d51000)
前面通过jaotc命令成功生成了 libHelloWorld.so。虽然命令里面参照 JEP 295 的示例将生成的文件后缀设置成了 so,但如果使用ldd命令查看,会发现它其实是一个静态链接库:
$ ldd libHelloWorld.so
statically linked
通过nm命令,可以看见代码段中的函数入口:
$ nm libHelloWorld.so
0000000000002420 t HelloWorld.()V
0000000000002520 t HelloWorld.main([Ljava/lang/String;)V
最后,需要执行时需要通过参数-XX:AOTLibrary参数指定需要加载的经过 aot 预编译好的共享库文件:
java -XX:AOTLibrary=./libHelloWorld.so HelloWorld
注意:虽然已经将整个 HelloWorld 类都通过 jaotc 编译成共享库文件,运行时仍然需要依赖原有的 HelloWorld.class 文件。
此时执行的输出,和之前不使用 AOT 的输出完全相同。
来把大的——将 java.base 模块编译成 AOT 库
JEP 295 中已经说明,在 Java 9 初始发布的时候,只保证 java.base 模块可以被编译成 AOT 库。
继续参照 JEP 295,创建 java.base-list.txt 文件,内容主要是排除一些编译有问题的方法,具体内容参照原文。
然后执行命令:
jaotc -J-XX:+UseCompressedOops -J-XX:+UseG1GC -J-Xmx4g --compile-for-tiered --info --compile-commands java.base-list.txt --output libjava.base-coop.so --module java.base
在笔者的机器上(i7-6600U + 16G 内存 + 256G NVMe SSD),排除上述方法之后,编译时间大约为 9 分多钟。
48878 methods compiled, 4 methods failed (497771 ms)
Parsing compiled code (1126 ms)
Processing metadata (15811 ms)
Preparing stubs binary (0 ms)
Preparing compiled binary (104 ms)
Creating binary: libjava.base-coop.o (5611 ms)
Creating shared library: libjava.base-coop.so (7306 ms)
Total time: 542536 ms
完成之后,就可以使用 AOT 版本的 java.base 模块:
java -XX:AOTLibrary=java_base/libjava.base-coop.so,./libHelloWorld.so HelloWorld
同样,针对 AOT,jvm 也新增了参数打印哪些方法是通过加载 AOT 预编译库执行。
java -XX:+PrintAOT -XX:AOTLibrary=java_base/libjava.base-coop.so,./libHelloWorld.so HelloWorld
输出可以和不使用 java.base 的 AOT 进行比较,发现不使用 java.base 的 AOT 库,只能会加载 libHelloWorld.so 中对应的方法。
$ java -XX:+PrintAOT -XX:AOTLibrary=./libHelloWorld.so HelloWorld
11 1 loaded ./libHelloWorld.so aot library
105 1 aot[ 1] HelloWorld.()V
105 2 aot[ 1] HelloWorld.main([Ljava/lang/String;)V
Hello World!
$ java -XX:+PrintAOT -XX:AOTLibrary=java_base/libjava.base-coop.so,./libHelloWorld.so HelloWorld
13 1 loaded java_base/libjava.base-coop.so aot library
13 2 loaded ./libHelloWorld.so aot library
[Found [Z in java_base/libjava.base-coop.so]
[Found [C in java_base/libjava.base-coop.so]
[Found [F in java_base/libjava.base-coop.so]
[Found [D in java_base/libjava.base-coop.so]
[Found [B in java_base/libjava.base-coop.so]
[Found [S in java_base/libjava.base-coop.so]
[Found [I in java_base/libjava.base-coop.so]
[Found [J in java_base/libjava.base-coop.so]
31 1 aot[ 1] java.lang.Object.()V
31 2 aot[ 1] java.lang.Object.finalize()V
...
输出太长,节选部分输出,我们可以看见 java 基础类及其方法都通过 AOT 的方式进行加载。
实用吗?
目前 AOT 的局限有:
仅支持 64 位 Linux 操作系统:这个问题不是很大,毕竟大部分线上服务器都能够满足;
操作系统需要预装 libelf 库,以确保能够生成 elf 文件:这个问题也不大,仅生成时需要;
AOT 编译和执行环境需要相同:毕竟是二进制文件,引入了平台相关性;
Java 9 最初发布时,只支持 java.base 模块可以编译成 AOT 库;
目前只支持 G1 和 Parallel GC 两种 GC 方式:前面没有提到,AOT 编译时的 JVM 参数和运行时需要相同,也包括 GC 方式,也就是说如果用了 AOT,JVM 实际运行时也只能使用这两种 GC 方式之一;
可能会无法编译通过动态生成 class 文件或者修改字节码的 java 代码(如 lambda 表达式、反射调用等):这个可能会比较坑,后面会讲到;
JVM 运行时参数设置必须和 AOT 库编译时相同;
AOT 可能带来的好处,是 JVM 加载这些已经预编译成二进制库之后,可以直接调用,而无需再将其运行时编译成二进制码。理论上,AOT 的方式,可以减少 JIT 带来的预热时间,减少 Java 应用长期给人带来的“第一次运行慢”感觉。
不过,本文使用的 HelloWorld 过于简单,无法通过对比得出 AOT 是否可以减少 JVM 初始化时间。笔者尝试对一个小型 springboot 应用进行 AOT 化,但是 springboot 框架本身无法在 Java 9 中运行。同时直接对 spring-core 的 jar 包执行 jaotc 也因为各种依赖问题而失败。
经过各种尝试,目前 Java 9 的 AOT 功能还处于很初步的阶段:
缺少 maven 等管理工具集成,无法方便的对项目指定 jar 或者 class 文件比构建 AOT 库;
大型框架还没有官方支持,构建 AOT 库难度比较高;
大型框架如果直接提供 AOT 库,可能会因为由特定平台构建,而在本地无法使用;
期待 Java 9 正式发布的时候,能够对 AOT 有更好的支持。
猜你喜欢
- 2024-09-19 “全栈2019”Java第一百一十二章:什么是闭包?
- 2024-09-19 Java两个Set集合判断是否有交集(java set求并集)
- 2024-09-19 从一道面试题说起:GET 请求能传图片吗?
- 2024-09-19 Java设计模式(二十):职责链模式(java责任链模式的应用场景)
- 2024-09-19 32位和64位的JVM应该用哪个?
- 2024-09-19 Mac下安装 JDK17(mac下安装nvm以及node)
- 2024-09-19 Java Web项目部署(二)——JDK、Tomcat
- 2024-09-19 Java Web项目部署(三)-MySQL8(javaweb连接mysql具体步骤)
- 2024-09-19 腾讯算法面试题:64匹马8个跑道需要多少轮才能选出最快的四匹?
- 2024-09-19 win7下绿色版mysql-5.7.18winx64如何配置
你 发表评论:
欢迎- 最近发表
-
- class版本不兼容错误原因分析(class更新)
- 甲骨文Oracle公司为Java的最新LTS版本做出改进
- 「版本发布」Minecraft Java开发版 1.19.4-pre1 发布
- java svn版本管理工具(svn软件版本管理)
- 我的世界1.8.10钻石在第几层(我的世界1.7.2钻石在哪层)
- Java开发高手必备:在电脑上轻松切换多个JDK版本
- 2022 年 Java 开发报告:Java 8 八年不到,开发者都在用什么?
- 开发java项目,选择哪个版本的JDK比较合适?
- Java版本选型终极指南:8 vs 17 vs 21特性对决!大龄程序员踩坑总结
- POI Excel导入(poi excel导入附件)
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)