java 位运算取8位_Java 9 AOT 试用:仅支持 64 位 Linux和java.base 模块编译
Java 9 引入了 aot 編譯方式,能夠?qū)?class 文件直接編譯成可執(zhí)行二進(jìn)制文件。目前 Java 9 的 early access 版本已經(jīng)提供了編譯工具,讓我們來看看它的功能吧。
注意:按照 JEP 295 描述,目前版本的 AOT,僅支持 64 位 Linux 操作系統(tǒng)。
jaotc 使用
首先需要下載最新的Java 9(JDK),本文編寫時(shí),最新版本是Build 152。下載好的JDK 只需要解壓即可使用,特別注意使用前設(shè)置好 PATH和JAVA_HOME兩個(gè)環(huán)境變量,避免和機(jī)器上已經(jīng)安裝的 JDK 混淆。筆者安裝到了 $HOME/bin/jdk-9,并設(shè)置了:
export PATH=~/bin/jdk-9/bin:$PATH
export JAVA_HOME=~/bin/jdk-9
需要使用jaotc,首先需要有個(gè)測試類,首先從 Hello World 開始:
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
代碼非常簡單,但是在執(zhí)行 jaotc 之前,還需要將其編譯成 class 文件,直接使用 javac 即可:
$ javac HelloWorld.java
執(zhí)行成功之后,會(huì)生成 HelloWorld.class 文件。此時(shí)直接使用 java 命令,已經(jīng)可以正常運(yùn)行這個(gè)類:
$ java HelloWorld
Hello World!
這時(shí),就可以基于這個(gè) class 文件,通過jaotc命令將其編譯成二進(jìn)制文件了。
$ jaotc --output libHelloWorld.so HelloWorld.class
如果一切正常,會(huì)生成 libHelloWorld.so 文件。
如果出現(xiàn)類似Exception in thread "main" java.lang.UnsatisfiedLinkError: /home/babydragon/bin/jdk-9/lib/libjelfshim.so: libelf.so.1: 無法打開共享對(duì)象文件: 沒有那個(gè)文件或目錄的錯(cuò)誤,是因?yàn)閖aotc需要依賴 libelf 動(dòng)態(tài)鏈接庫來創(chuàng)建 elf 文件(最終生成的 libHelloWorld.so 文件是一個(gè)靜態(tài)鏈接的 elf 文件)。筆者使用的是 Gentoo 系統(tǒng),需要安裝 dev-libs/elfutils 包,以提供 libelf.so 這個(gè)動(dòng)態(tài)連接庫。安裝之后可以通過ldd命令進(jìn)行確認(rèn):
$ 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 的示例將生成的文件后綴設(shè)置成了 so,但如果使用ldd命令查看,會(huì)發(fā)現(xiàn)它其實(shí)是一個(gè)靜態(tài)鏈接庫:
$ ldd libHelloWorld.so
statically linked
通過nm命令,可以看見代碼段中的函數(shù)入口:
$ nm libHelloWorld.so
0000000000002420 t HelloWorld.()V
0000000000002520 t HelloWorld.main([Ljava/lang/String;)V
最后,需要執(zhí)行時(shí)需要通過參數(shù)-XX:AOTLibrary參數(shù)指定需要加載的經(jīng)過 aot 預(yù)編譯好的共享庫文件:
java -XX:AOTLibrary=./libHelloWorld.so HelloWorld
注意:雖然已經(jīng)將整個(gè) HelloWorld 類都通過 jaotc 編譯成共享庫文件,運(yùn)行時(shí)仍然需要依賴原有的 HelloWorld.class 文件。
此時(shí)執(zhí)行的輸出,和之前不使用 AOT 的輸出完全相同。
來把大的——將 java.base 模塊編譯成 AOT 庫
JEP 295 中已經(jīng)說明,在 Java 9 初始發(fā)布的時(shí)候,只保證 java.base 模塊可以被編譯成 AOT 庫。
繼續(xù)參照 JEP 295,創(chuàng)建 java.base-list.txt 文件,內(nèi)容主要是排除一些編譯有問題的方法,具體內(nèi)容參照原文。
然后執(zhí)行命令:
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
在筆者的機(jī)器上(i7-6600U + 16G 內(nèi)存 + 256G NVMe SSD),排除上述方法之后,編譯時(shí)間大約為 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
同樣,針對(duì) AOT,jvm 也新增了參數(shù)打印哪些方法是通過加載 AOT 預(yù)編譯庫執(zhí)行。
java -XX:+PrintAOT -XX:AOTLibrary=java_base/libjava.base-coop.so,./libHelloWorld.so HelloWorld
輸出可以和不使用 java.base 的 AOT 進(jìn)行比較,發(fā)現(xiàn)不使用 java.base 的 AOT 庫,只能會(huì)加載 libHelloWorld.so 中對(duì)應(yīng)的方法。
$ 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
...
輸出太長,節(jié)選部分輸出,我們可以看見 java 基礎(chǔ)類及其方法都通過 AOT 的方式進(jìn)行加載。
實(shí)用嗎?
目前 AOT 的局限有:
僅支持 64 位 Linux 操作系統(tǒng):這個(gè)問題不是很大,畢竟大部分線上服務(wù)器都能夠滿足;
操作系統(tǒng)需要預(yù)裝 libelf 庫,以確保能夠生成 elf 文件:這個(gè)問題也不大,僅生成時(shí)需要;
AOT 編譯和執(zhí)行環(huán)境需要相同:畢竟是二進(jìn)制文件,引入了平臺(tái)相關(guān)性;
Java 9 最初發(fā)布時(shí),只支持 java.base 模塊可以編譯成 AOT 庫;
目前只支持 G1 和 Parallel GC 兩種 GC 方式:前面沒有提到,AOT 編譯時(shí)的 JVM 參數(shù)和運(yùn)行時(shí)需要相同,也包括 GC 方式,也就是說如果用了 AOT,JVM 實(shí)際運(yùn)行時(shí)也只能使用這兩種 GC 方式之一;
可能會(huì)無法編譯通過動(dòng)態(tài)生成 class 文件或者修改字節(jié)碼的 java 代碼(如 lambda 表達(dá)式、反射調(diào)用等):這個(gè)可能會(huì)比較坑,后面會(huì)講到;
JVM 運(yùn)行時(shí)參數(shù)設(shè)置必須和 AOT 庫編譯時(shí)相同;
AOT 可能帶來的好處,是 JVM 加載這些已經(jīng)預(yù)編譯成二進(jìn)制庫之后,可以直接調(diào)用,而無需再將其運(yùn)行時(shí)編譯成二進(jìn)制碼。理論上,AOT 的方式,可以減少 JIT 帶來的預(yù)熱時(shí)間,減少 Java 應(yīng)用長期給人帶來的“第一次運(yùn)行慢”感覺。
不過,本文使用的 HelloWorld 過于簡單,無法通過對(duì)比得出 AOT 是否可以減少 JVM 初始化時(shí)間。筆者嘗試對(duì)一個(gè)小型 springboot 應(yīng)用進(jìn)行 AOT 化,但是 springboot 框架本身無法在 Java 9 中運(yùn)行。同時(shí)直接對(duì) spring-core 的 jar 包執(zhí)行 jaotc 也因?yàn)楦鞣N依賴問題而失敗。
經(jīng)過各種嘗試,目前 Java 9 的 AOT 功能還處于很初步的階段:
缺少 maven 等管理工具集成,無法方便的對(duì)項(xiàng)目指定 jar 或者 class 文件比構(gòu)建 AOT 庫;
大型框架還沒有官方支持,構(gòu)建 AOT 庫難度比較高;
大型框架如果直接提供 AOT 庫,可能會(huì)因?yàn)橛商囟ㄆ脚_(tái)構(gòu)建,而在本地?zé)o法使用;
期待 Java 9 正式發(fā)布的時(shí)候,能夠?qū)?AOT 有更好的支持。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的java 位运算取8位_Java 9 AOT 试用:仅支持 64 位 Linux和java.base 模块编译的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux 线程_Linux 多线程编程
- 下一篇: linux docker安装svn,使用