GraalVM系列Native?Image?Basics靜態(tài)分析
引言
native image
是GraalVM中提供的一個命令,可以把字節(jié)碼文件或Jar包編譯成為一個二進制可執(zhí)行文件,同時它自己也是用Java語言開發(fā)的(實現了Java的語言自舉)。
Build Time vs Run Time
native image
在編譯時,可能會執(zhí)行類中的某些代碼,比如給類中的static屬性賦值(正常來說應該時運行時才去賦值的,現在是編譯時可能就被賦值了,這里說的編譯不是javac)。
通常,在Java中,一個類在第一次被使用時才會進行初始化,但是我們使用native image
時就有可能直接進行類的初始化,我們把這個機制叫做build-time initialized,而二進制可執(zhí)行文件在運行時,即便是第一次使用這個類時也都不會觸發(fā)類的初始化了。
默認情況下native image
是不會執(zhí)行編譯期類初始化的,我們可以通過兩種方式來在編譯時觸發(fā)類的初始化:
- 在執(zhí)行
native-image
時傳入--initialize-at-build-time=<class>
- 在一個能編譯時初始化的類中去使用其他的類
native image
會把常用的JDK中的類在編譯時進行初始化,比如java.lang.String
,java.util.**
,等等。
編譯期類的初始化是一個專業(yè)特征,并不是所有類都適合。
請看下面的Demo加深理解:
public class HelloWorld { static class Greeter { static { System.out.println("Greeter is getting ready!"); } public static void greet() { System.out.println("Hello, World!"); } } public static void main(String[] args) { Greeter.greet(); } }
使用Java原本的方式編譯并運行:
javac HelloWorld.java java HelloWorld Greeter is getting ready! Hello, World!
然后,我們把它編譯為一個本地可執(zhí)行文件,然后執(zhí)行這個文件:
native-image HelloWorld =============================================================== GraalVM Native Image: Generating 'helloworld' (executable)... ================================================================ ... Finished generating 'helloworld' in 14.9s.
./helloworld Greeter is getting ready! Hello, World!
我們發(fā)現,上述兩個過程都是在運行時才會對HelloWorld類進行初始化,所以默認情況下不會進行類的初始化。
我們通過添加--initialize-at-build-time=HelloWorld\$Greeter
來看看編譯期類初始化是怎樣的:
native-image HelloWorld --initialize-at-build-time=HelloWorld\$Greeter ====================================================================== GraalVM Native Image: Generating 'helloworld' (executable)... ====================================================================== Greeter is getting ready! ... Finished generating 'helloworld' in 13.6s.
./helloworld Hello, World!
我們發(fā)現Greeter is getting ready!
是在編譯時打印出來的,而真正在運行時由于HelloWorld類已經被初始化了,所以就沒有再初始化了。而在編譯時類初始化過程中被賦值的靜態(tài)屬性,會保存在二進制可執(zhí)行文件中的image heap中。
Native Image Heap
Native Image heap也可以叫做image heap,它包含了:
- 在編譯時創(chuàng)建出來的對象
- 在二進制文件中使用到的類對象(Class對象)
- 嵌入在方法中的對象常量
可以通過編譯時類初始化把一個對象放入image heap中:
class Example { private static final String message; static { message = System.getProperty("message"); } public static void main(String[] args) { System.out.println("Hello, World! My message is: " + message); } }
正常用java運行:
javac Example.java java -Dmessage=hi Example Hello, World! My message is: hi
java -Dmessage=hello Example Hello, World! My message is: hello
java Example Hello, World! My message is: null
而如果使用編譯期類初始化:
native-image Example --initialize-at-build-time=Example -Dmessage=native ======================================================================== GraalVM Native Image: Generating 'example' (executable)... ======================================================================== ... Finished generating 'example' in 19.0s.
./example Hello, World! My message is: native
./example -Dmessage=aNewMessage Hello, World! My message is: native
Example類的初始化在編譯期被執(zhí)行了,并且會創(chuàng)建一個String對象賦值給message屬性,并且把它存進了image heap中,運行的時候就直接從image heap中拿出來用了,忽略了運行時指定的-Dmessage
靜態(tài)分析
native image
在執(zhí)行時,會先進行靜態(tài)分析,靜態(tài)分析會掃描出當前應用程序中真正用到了哪些類、方法、屬性(其實通常我們一個應用中很多類,特別是依賴的第三方Jar包中的類,是沒有被應用程序使用的),這些元素稱之為reachable code。
靜態(tài)分析包含兩個部分:
- 掃描一個方法的字節(jié)碼(比如main方法),找到它可達的其他元素
- 從native image heap中的對象開始掃描,找到其他可達的元素
只有可達元素才能包含到二進制可執(zhí)行文件中,一個二進制可執(zhí)行文件編譯出來后,運行過程中就不能再有新元素被添加進去了,比如動態(tài)類加載,我們把這個叫做closed-world。
官網原文 https://www.graalvm.org/latest/reference-manual/native-image/basics/
以上就是GraalVM系列Native Image Basics靜態(tài)分析的詳細內容,更多關于GraalVM Native Image Basics的資料請關注腳本之家其它相關文章!
相關文章
Maven Plugin的@Mojo和@Execute的具體使用
本文主要介紹了Maven Plugin的@Mojo和@Execute的具體使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09mybatisPlus實現邏輯刪除,自動生成創(chuàng)建時間和更新時間方式
MyBatisPlus框架中,通過@TableField(fill=FieldFill.INSERT)和@TableField(fill=FieldFill.UPDATE)注解可以實現在插入和更新時自動填充字段,比如創(chuàng)建時間和更新時間,使用@TableLogic注解標識邏輯刪除字段2024-09-09