首頁 > 軟體

GraalVM系列Native Image Basics靜態分析

2023-11-01 18:00:43

引言

native image是GraalVM中提供的一個命令,可以把位元組碼檔案或Jar包編譯成為一個二進位制可執行檔案,同時它自己也是用Java語言開發的(實現了Java的語言自舉)。

Build Time vs Run Time

native image在編譯時,可能會執行類中的某些程式碼,比如給類中的static屬性賦值(正常來說應該時執行時才去賦值的,現在是編譯時可能就被賦值了,這裡說的編譯不是javac)。

通常,在Java中,一個類在第一次被使用時才會進行初始化,但是我們使用native image時就有可能直接進行類的初始化,我們把這個機制叫做build-time initialized,而二進位制可執行檔案在執行時,即便是第一次使用這個類時也都不會觸發類的初始化了。

預設情況下native image是不會執行編譯期類初始化的,我們可以通過兩種方式來在編譯時觸發類的初始化:

  • 在執行native-image時傳入--initialize-at-build-time=<class>
  • 在一個能編譯時初始化的類中去使用其他的類

native image會把常用的JDK中的類在編譯時進行初始化,比如java.lang.Stringjava.util.**,等等。

編譯期類的初始化是一個專業特徵,並不是所有類都適合。

請看下面的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!

然後,我們把它編譯為一個本地可執行檔案,然後執行這個檔案:

native-image HelloWorld
===============================================================
GraalVM Native Image: Generating 'helloworld' (executable)...
================================================================
...
Finished generating 'helloworld' in 14.9s.
./helloworld 
Greeter is getting ready!
Hello, World!

我們發現,上述兩個過程都是在執行時才會對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!

我們發現Greeter is getting ready!是在編譯時列印出來的,而真正在執行時由於HelloWorld類已經被初始化了,所以就沒有再初始化了。而在編譯時類初始化過程中被賦值的靜態屬性,會儲存在二進位制可執行檔案中的image heap中。

Native Image Heap

Native Image heap也可以叫做image heap,它包含了:

  • 在編譯時建立出來的物件
  • 在二進位制檔案中使用到的類物件(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類的初始化在編譯期被執行了,並且會建立一個String物件賦值給message屬性,並且把它存進了image heap中,執行的時候就直接從image heap中拿出來用了,忽略了執行時指定的-Dmessage

靜態分析

native image在執行時,會先進行靜態分析,靜態分析會掃描出當前應用程式中真正用到了哪些類、方法、屬性(其實通常我們一個應用中很多類,特別是依賴的第三方Jar包中的類,是沒有被應用程式使用的),這些元素稱之為reachable code

靜態分析包含兩個部分:

  • 掃描一個方法的位元組碼(比如main方法),找到它可達的其他元素
  • 從native image heap中的物件開始掃描,找到其他可達的元素

只有可達元素才能包含到二進位制可執行檔案中,一個二進位制可執行檔案編譯出來後,執行過程中就不能再有新元素被新增進去了,比如動態類載入,我們把這個叫做closed-world。

官網原文  https://www.graalvm.org/latest/reference-manual/native-image/basics/

以上就是GraalVM系列Native Image Basics靜態分析的詳細內容,更多關於GraalVM Native Image Basics的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com