java 位运算取8位_Java 9 AOT 试用:仅支持 64 位 Linux和java.base 模块编译
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 有更好的支持。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的java 位运算取8位_Java 9 AOT 试用:仅支持 64 位 Linux和java.base 模块编译的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux 线程_Linux 多线程编程
- 下一篇: python upper_Python