GraalVM系列Native?Image?Basics靜態(tài)分析
引言
native image
是GraalVM中提供的一個(gè)命令,可以把字節(jié)碼文件或Jar包編譯成為一個(gè)二進(jìn)制可執(zhí)行文件,同時(shí)它自己也是用Java語(yǔ)言開(kāi)發(fā)的(實(shí)現(xiàn)了Java的語(yǔ)言自舉)。
Build Time vs Run Time
native image
在編譯時(shí),可能會(huì)執(zhí)行類(lèi)中的某些代碼,比如給類(lèi)中的static屬性賦值(正常來(lái)說(shuō)應(yīng)該時(shí)運(yùn)行時(shí)才去賦值的,現(xiàn)在是編譯時(shí)可能就被賦值了,這里說(shuō)的編譯不是javac)。
通常,在Java中,一個(gè)類(lèi)在第一次被使用時(shí)才會(huì)進(jìn)行初始化,但是我們使用native image
時(shí)就有可能直接進(jìn)行類(lèi)的初始化,我們把這個(gè)機(jī)制叫做build-time initialized,而二進(jìn)制可執(zhí)行文件在運(yùn)行時(shí),即便是第一次使用這個(gè)類(lèi)時(shí)也都不會(huì)觸發(fā)類(lèi)的初始化了。
默認(rèn)情況下native image
是不會(huì)執(zhí)行編譯期類(lèi)初始化的,我們可以通過(guò)兩種方式來(lái)在編譯時(shí)觸發(fā)類(lèi)的初始化:
- 在執(zhí)行
native-image
時(shí)傳入--initialize-at-build-time=<class>
- 在一個(gè)能編譯時(shí)初始化的類(lèi)中去使用其他的類(lèi)
native image
會(huì)把常用的JDK中的類(lèi)在編譯時(shí)進(jìn)行初始化,比如java.lang.String
,java.util.**
,等等。
編譯期類(lèi)的初始化是一個(gè)專(zhuān)業(yè)特征,并不是所有類(lèi)都適合。
請(qǐng)看下面的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原本的方式編譯并運(yùn)行:
javac HelloWorld.java java HelloWorld Greeter is getting ready! Hello, World!
然后,我們把它編譯為一個(gè)本地可執(zhí)行文件,然后執(zhí)行這個(gè)文件:
native-image HelloWorld =============================================================== GraalVM Native Image: Generating 'helloworld' (executable)... ================================================================ ... Finished generating 'helloworld' in 14.9s.
./helloworld Greeter is getting ready! Hello, World!
我們發(fā)現(xiàn),上述兩個(gè)過(guò)程都是在運(yùn)行時(shí)才會(huì)對(duì)HelloWorld類(lèi)進(jìn)行初始化,所以默認(rèn)情況下不會(huì)進(jìn)行類(lèi)的初始化。
我們通過(guò)添加--initialize-at-build-time=HelloWorld\$Greeter
來(lái)看看編譯期類(lèi)初始化是怎樣的:
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ā)現(xiàn)Greeter is getting ready!
是在編譯時(shí)打印出來(lái)的,而真正在運(yùn)行時(shí)由于HelloWorld類(lèi)已經(jīng)被初始化了,所以就沒(méi)有再初始化了。而在編譯時(shí)類(lèi)初始化過(guò)程中被賦值的靜態(tài)屬性,會(huì)保存在二進(jìn)制可執(zhí)行文件中的image heap中。
Native Image Heap
Native Image heap也可以叫做image heap,它包含了:
- 在編譯時(shí)創(chuàng)建出來(lái)的對(duì)象
- 在二進(jìn)制文件中使用到的類(lèi)對(duì)象(Class對(duì)象)
- 嵌入在方法中的對(duì)象常量
可以通過(guò)編譯時(shí)類(lèi)初始化把一個(gè)對(duì)象放入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運(yùn)行:
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
而如果使用編譯期類(lèi)初始化:
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類(lèi)的初始化在編譯期被執(zhí)行了,并且會(huì)創(chuàng)建一個(gè)String對(duì)象賦值給message屬性,并且把它存進(jìn)了image heap中,運(yùn)行的時(shí)候就直接從image heap中拿出來(lái)用了,忽略了運(yùn)行時(shí)指定的-Dmessage
靜態(tài)分析
native image
在執(zhí)行時(shí),會(huì)先進(jìn)行靜態(tài)分析,靜態(tài)分析會(huì)掃描出當(dāng)前應(yīng)用程序中真正用到了哪些類(lèi)、方法、屬性(其實(shí)通常我們一個(gè)應(yīng)用中很多類(lèi),特別是依賴(lài)的第三方Jar包中的類(lèi),是沒(méi)有被應(yīng)用程序使用的),這些元素稱(chēng)之為reachable code。
靜態(tài)分析包含兩個(gè)部分:
- 掃描一個(gè)方法的字節(jié)碼(比如main方法),找到它可達(dá)的其他元素
- 從native image heap中的對(duì)象開(kāi)始掃描,找到其他可達(dá)的元素
只有可達(dá)元素才能包含到二進(jìn)制可執(zhí)行文件中,一個(gè)二進(jìn)制可執(zhí)行文件編譯出來(lái)后,運(yùn)行過(guò)程中就不能再有新元素被添加進(jìn)去了,比如動(dòng)態(tài)類(lèi)加載,我們把這個(gè)叫做closed-world。
官網(wǎng)原文 https://www.graalvm.org/latest/reference-manual/native-image/basics/
以上就是GraalVM系列Native Image Basics靜態(tài)分析的詳細(xì)內(nèi)容,更多關(guān)于GraalVM Native Image Basics的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java Feign微服務(wù)接口調(diào)用方法詳細(xì)講解
現(xiàn)如今微服務(wù)架構(gòu)十分流行,而采用微服務(wù)構(gòu)建系統(tǒng)也會(huì)帶來(lái)更清晰的業(yè)務(wù)劃分和可擴(kuò)展性。java如果使用微服務(wù)就離不開(kāi)springcloud,我這里是把服務(wù)注冊(cè)到nacos上,各個(gè)服務(wù)之間的調(diào)用使用feign2023-01-01Maven Plugin的@Mojo和@Execute的具體使用
本文主要介紹了Maven Plugin的@Mojo和@Execute的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Java計(jì)算兩個(gè)程序運(yùn)行時(shí)間的實(shí)例
下面小編就為大家?guī)?lái)一篇Java計(jì)算兩個(gè)程序運(yùn)行時(shí)間的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04Java單線程程序?qū)崿F(xiàn)實(shí)現(xiàn)簡(jiǎn)單聊天功能
這篇文章主要介紹了Java單線程程序?qū)崿F(xiàn)實(shí)現(xiàn)簡(jiǎn)單聊天功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10Java的項(xiàng)目構(gòu)建工具M(jìn)aven的配置和使用教程
Maven是Java世界中的項(xiàng)目管理和構(gòu)建自動(dòng)化工具,基于POM項(xiàng)目對(duì)象模型的思想,下面我們就具體來(lái)看一下具體的Java的項(xiàng)目構(gòu)建工具M(jìn)aven的配置和使用教程:2016-05-05SpringBoot過(guò)濾器與攔截器深入分析實(shí)現(xiàn)方法
大家應(yīng)該都曉得實(shí)現(xiàn)過(guò)濾器需要實(shí)現(xiàn) javax.servlet.Filter 接口,而攔截器會(huì)在處理指定請(qǐng)求之前和之后進(jìn)行相關(guān)操作,配置攔截器需要兩步,本文通過(guò)實(shí)例代碼給大家介紹SpringBoot 過(guò)濾器和攔截器的相關(guān)知識(shí),感興趣的朋友一起看看吧2022-11-11mybatisPlus實(shí)現(xiàn)邏輯刪除,自動(dòng)生成創(chuàng)建時(shí)間和更新時(shí)間方式
MyBatisPlus框架中,通過(guò)@TableField(fill=FieldFill.INSERT)和@TableField(fill=FieldFill.UPDATE)注解可以實(shí)現(xiàn)在插入和更新時(shí)自動(dòng)填充字段,比如創(chuàng)建時(shí)間和更新時(shí)間,使用@TableLogic注解標(biāo)識(shí)邏輯刪除字段2024-09-09